+ {
+ "Item 0",
+ "Item 1",
+ "Item 2",
+ "Item 3",
+ "Item 4",
+ };
+
+ CarouselView carouselView = new CarouselView
+ {
+ ItemsSource = carouselItems,
+ AutomationId = "carouselview",
+ ItemsUpdatingScrollMode = ItemsUpdatingScrollMode.KeepScrollOffset,
+ HeightRequest = 300,
+ ItemTemplate = new DataTemplate(() =>
+ {
+ var grid = new Grid
+ {
+ Padding = 10
+ };
+
+ var label = new Label
+ {
+ VerticalOptions = LayoutOptions.Center,
+ HorizontalOptions = LayoutOptions.Center,
+ FontSize = 18,
+ };
+ label.SetBinding(Label.TextProperty, ".");
+ label.SetBinding(Label.AutomationIdProperty, ".");
+
+ grid.Children.Add(label);
+ return grid;
+ }),
+ HorizontalOptions = LayoutOptions.Fill,
+ };
+
+ var indicatorView = new IndicatorView
+ {
+ HorizontalOptions = LayoutOptions.Center,
+ VerticalOptions = LayoutOptions.Center
+ };
+ carouselView.IndicatorView = indicatorView;
+
+ var addButton = new Button
+ {
+ Text = "Add item",
+ AutomationId = "AddButton",
+ Margin = new Thickness(20),
+ };
+
+ addButton.Clicked += (sender, e) =>
+ {
+ carouselItems.Insert(0, "NewItem");
+ };
+
+ verticalStackLayout.Children.Add(addButton);
+ verticalStackLayout.Children.Add(carouselView);
+ verticalStackLayout.Children.Add(indicatorView);
+
+ Content = verticalStackLayout;
+ }
+}
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue29898.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue29898.cs
new file mode 100644
index 000000000000..7bc85c87b19c
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue29898.cs
@@ -0,0 +1,54 @@
+using System.Collections.ObjectModel;
+
+namespace Maui.Controls.Sample.Issues;
+[Issue(IssueTracker.Github, 29898, "[iOS, macOS] StrokeDashArray on Border does not reset when set to null", PlatformAffected.iOS)]
+public class Issue29898 : ContentPage
+{
+ public Issue29898()
+ {
+ var border = new Border
+ {
+ HeightRequest = 200,
+ WidthRequest = 200,
+ BackgroundColor = Colors.LightGray,
+ Stroke = Colors.Blue,
+ StrokeThickness = 5,
+ StrokeDashArray = new DoubleCollection { 10, 5 }
+ };
+
+ var button = new Button
+ {
+ Text = "Clear StrokeDashArray",
+ AutomationId = "ClearDashButton",
+ HorizontalOptions = LayoutOptions.Center
+ };
+
+ var button2 = new Button
+ {
+ Text = "Set StrokeDashArray",
+ AutomationId = "SetDashButton",
+ HorizontalOptions = LayoutOptions.Center
+ };
+
+ button.Clicked += (s, e) =>
+ {
+ border.StrokeDashArray = null;
+ };
+
+ button2.Clicked += (s, e) =>
+ {
+ border.StrokeDashArray = new DoubleCollection { 5, 2 };
+ };
+
+ var layout = new VerticalStackLayout
+ {
+ Spacing = 25,
+ Padding = new Thickness(30, 60, 30, 30),
+ VerticalOptions = LayoutOptions.Center,
+ Children = { border, button, button2 }
+ };
+
+ Content = layout;
+ }
+
+}
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue30081.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue30081.cs
new file mode 100644
index 000000000000..5fee42924f74
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue30081.cs
@@ -0,0 +1,133 @@
+using System.Collections.ObjectModel;
+using System.ComponentModel;
+using System.Runtime.CompilerServices;
+
+namespace Maui.Controls.Sample.Issues;
+
+[Issue(IssueTracker.Github, 30081, "[Android] ScrollView scroll position changes unexpectedly when Orientation is set to Horizontal and FlowDirection is RTL at runtime", PlatformAffected.Android)]
+
+public class Issue30081 : ContentPage
+{
+ readonly ScrollViewViewModel _viewModel;
+
+ public Issue30081()
+ {
+ _viewModel = new ScrollViewViewModel();
+ BindingContext = _viewModel;
+
+ // Create Grid (same as XAML structure)
+ var grid = new Grid
+ {
+ RowDefinitions =
+ {
+ new RowDefinition { Height = GridLength.Star },
+ new RowDefinition { Height = GridLength.Auto }
+ }
+ };
+
+ // Create ContentView and bind its Content
+ var scrollViewContent = new ContentView
+ {
+ AutomationId = "ScrollViewContent"
+ };
+ scrollViewContent.SetBinding(ContentView.ContentProperty, nameof(ScrollViewViewModel.Content));
+
+ // Create ScrollView and bind its Orientation
+ var scrollView = new ScrollView
+ {
+ Content = scrollViewContent,
+ FlowDirection = FlowDirection.RightToLeft
+ };
+ scrollView.SetBinding(ScrollView.OrientationProperty, nameof(ScrollViewViewModel.Orientation));
+
+ // Add ScrollView to Grid row 0
+ grid.Add(scrollView);
+ Grid.SetRow(scrollView, 0);
+
+ // Create Button
+ var button = new Button
+ {
+ Text = "Toggle Orientation",
+ AutomationId = "ToggleOrientationButton"
+ };
+ button.Clicked += Button_Clicked;
+
+ // Add Button to Grid row 1
+ grid.Add(button);
+ Grid.SetRow(button, 1);
+
+ // Set grid as the page content
+ Content = grid;
+ }
+
+ void Button_Clicked(object sender, EventArgs e)
+ {
+ if (_viewModel.Orientation == ScrollOrientation.Vertical)
+ _viewModel.Orientation = ScrollOrientation.Horizontal;
+ else
+ _viewModel.Orientation = ScrollOrientation.Vertical;
+ }
+}
+
+public class ScrollViewViewModel : INotifyPropertyChanged
+{
+ string _contentText;
+ ScrollOrientation _orientation = ScrollOrientation.Vertical;
+
+ public ScrollOrientation Orientation
+ {
+ get => _orientation;
+ set
+ {
+ if (_orientation != value)
+ {
+ _orientation = value;
+ OnPropertyChanged();
+ }
+ }
+ }
+
+ View _content;
+
+ public ScrollViewViewModel()
+ {
+ _contentText = string.Empty;
+ Content = new Label
+ {
+ Text = string.Join(Environment.NewLine, Enumerable.Range(1, 100).Select(i => $"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed euismod, urna eu tincidunt consectetur, nisi nisl aliquam enim, eget facilisis enim nisl nec elit . Sed euismod, urna eu tincidunt consectetur, nisi nisl aliquam enim Eget facilisis enim nisl nec elit Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae. Nullam ac erat at dui laoreet aliquet. Praesent euismod, justo at dictum facilisis, urna erat dictum enim. {i}")),
+ FontSize = 18,
+ Padding = 10
+ };
+ }
+
+ public string ContentText
+ {
+ get => _contentText;
+ set
+ {
+ if (_contentText != value)
+ {
+ _contentText = value;
+ Content = new Label { Text = _contentText }; // Update Content when ContentText changes
+ OnPropertyChanged();
+ }
+ }
+ }
+
+ public View Content
+ {
+ get => _content;
+ set
+ {
+ if (_content != value)
+ {
+ _content = value;
+ OnPropertyChanged();
+ }
+ }
+ }
+ public event PropertyChangedEventHandler PropertyChanged;
+
+ protected void OnPropertyChanged([CallerMemberName] string propertyName = "") =>
+ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
+}
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue30248.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue30248.cs
new file mode 100644
index 000000000000..fa2376317792
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue30248.cs
@@ -0,0 +1,56 @@
+namespace Maui.Controls.Sample.Issues;
+
+[Issue(IssueTracker.Github, 30248, "TitleBar, MacCatalyst - content is not aligned to left on fullscreen", PlatformAffected.macOS)]
+
+public class Issue30248 : ContentPage
+{
+ public Issue30248()
+ {
+ Title = "Issue 30248";
+
+ // Create TitleBar
+ var titleBar = new TitleBar
+ {
+ Title = "Maui App",
+ Subtitle = "Hello, World!",
+ ForegroundColor = Colors.Red,
+ HeightRequest = 48
+ };
+
+ titleBar.LeadingContent = new Image {Source = "dotnet_bot.png", HeightRequest = 24};
+
+ // Set the TitleBar on the current Window when this page appears
+ this.Loaded += (sender, e) =>
+ {
+ if (Window != null)
+ {
+ Window.TitleBar = titleBar;
+ }
+ };
+
+ // Create the page content with a Label
+ Content = new VerticalStackLayout
+ {
+ Spacing = 25,
+ Padding = new Thickness(30),
+ VerticalOptions = LayoutOptions.Center,
+ Children =
+ {
+ new Label
+ {
+ Text = "TitleBar should be aligned to the left in fullscreen mode",
+ AutomationId = "TitleBarAlignmentLabel",
+ FontSize = 32,
+ HorizontalOptions = LayoutOptions.Center
+ },
+ new Button
+ {
+ Text = "Empty Button",
+ AutomationId = "EmptyButton",
+ HorizontalOptions = LayoutOptions.Center
+ }
+ }
+ };
+ }
+}
+
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue30515.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue30515.cs
new file mode 100644
index 000000000000..1bbf8a89421e
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue30515.cs
@@ -0,0 +1,135 @@
+namespace Maui.Controls.Sample.Issues;
+
+[Issue(IssueTracker.Github, 30515, "[iOS] WebView.Reload() with HtmlWebViewSource returns WebNavigationResult.Failure in Navigated event", PlatformAffected.iOS)]
+public class Issue30515 : ContentPage
+{
+ WebView webView;
+ Label statusTextLabel;
+ Label statusValueLabel;
+
+ public Issue30515()
+ {
+ Title = "Issue 30515";
+
+ // Create the navigation status labels - split into static text and dynamic value
+ statusTextLabel = new Label
+ {
+ Text = "Status:",
+ FontSize = 14,
+ TextColor = Colors.Gray,
+ VerticalOptions = LayoutOptions.Center
+ };
+
+ statusValueLabel = new Label
+ {
+ Text = "Ready",
+ AutomationId = "NavigationStatusLabel",
+ FontSize = 14,
+ TextColor = Colors.Blue,
+ VerticalOptions = LayoutOptions.Center
+ };
+
+ // Create a horizontal stack for the status display
+ var statusContainer = new StackLayout
+ {
+ Orientation = StackOrientation.Horizontal,
+ Spacing = 5,
+ HorizontalOptions = LayoutOptions.Center,
+ Children = { statusTextLabel, statusValueLabel }
+ };
+
+ // Create the WebView
+ webView = new WebView
+ {
+ HeightRequest = 300,
+ WidthRequest = 400,
+ Source = new HtmlWebViewSource
+ {
+ Html = @"
+
+
+ HTML WebView Source
+
+
+ WebView Feature Matrix
+ This page demonstrates various capabilities of the .NET MAUI WebView control, such as:
+
+ Rendering HTML content
+ Executing JavaScript
+ Cookie management
+ Back/Forward navigation
+
+ Test Content
+
+ This is a longer body paragraph to help test the EvaluateJavaScript functionality
+ and how it extracts body text. You can use this text to verify substring operations and test scrolling
+ or formatting in the WebView.
+
+
+
+ Try interacting with navigation buttons, loading multiple pages, or checking cookie behavior.
+
+
+ Navigation Test Links
+ Click these links to test URL navigation:
+
+
+ Generated for testing WebView features.
+
+ "
+ }
+ };
+
+ // Set up event handlers
+ webView.Navigated += OnWebViewNavigated;
+
+ // Create the reload button
+ var reloadButton = new Button
+ {
+ Text = "Reload",
+ AutomationId = "ReloadButton",
+ HorizontalOptions = LayoutOptions.Center
+ };
+ reloadButton.Clicked += OnReloadClicked;
+
+ // Add all elements to the page
+ Content = new StackLayout
+ {
+ Padding = new Thickness(10),
+ Spacing = 10,
+ Children =
+ {
+ statusContainer,
+ webView,
+ new StackLayout
+ {
+ Orientation = StackOrientation.Horizontal,
+ Spacing = 10,
+ HorizontalOptions = LayoutOptions.Center,
+ Children =
+ {
+ reloadButton
+ }
+ }
+ }
+ };
+ }
+
+ void OnReloadClicked(object sender, EventArgs e)
+ {
+ statusValueLabel.Text = "Reloading...";
+ webView.Reload();
+ }
+
+ void OnWebViewNavigated(object sender, WebNavigatedEventArgs e)
+ {
+ statusValueLabel.Text = e.Result.ToString();
+ statusValueLabel.TextColor = Colors.Green;
+ }
+}
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue31065.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue31065.cs
new file mode 100644
index 000000000000..37e3442e1ba3
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue31065.cs
@@ -0,0 +1,54 @@
+namespace Maui.Controls.Sample.Issues;
+
+[Issue(IssueTracker.Github, 31065, "IndicatorView square shape does not update on load or dynamically", PlatformAffected.iOS | PlatformAffected.macOS)]
+public class Issue31065 : ContentPage
+{
+ IndicatorView _indicator;
+ public Issue31065()
+ {
+ var carousel = new CarouselView
+ {
+ ItemsSource = new[] { "Item 1", "Item 2", "Item 3" },
+ HeightRequest = 250,
+ };
+
+ _indicator = new IndicatorView
+ {
+ IndicatorsShape = IndicatorShape.Square,
+ SelectedIndicatorColor = Colors.Blue,
+ IndicatorSize = 30
+ };
+
+ Button button = new Button
+ {
+ Text = "Change indicator shape",
+ HorizontalOptions = LayoutOptions.Center,
+ AutomationId = "ChangeIndicatorShapeButton",
+ Margin = new Thickness(0, 20, 0, 0)
+ };
+
+ button.Command = new Command(() =>
+ {
+ if (_indicator.IndicatorsShape == IndicatorShape.Square)
+ {
+ _indicator.IndicatorsShape = IndicatorShape.Circle;
+ }
+ else
+ {
+ _indicator.IndicatorsShape = IndicatorShape.Square;
+ }
+ });
+
+ carousel.IndicatorView = _indicator;
+
+ Content = new VerticalStackLayout
+ {
+ Children =
+ {
+ carousel,
+ _indicator,
+ button
+ }
+ };
+ }
+}
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue32221.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue32221.cs
new file mode 100644
index 000000000000..64476e8bd608
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue32221.cs
@@ -0,0 +1,83 @@
+using System.Collections.ObjectModel;
+
+namespace Maui.Controls.Sample.Issues;
+
+[Issue(IssueTracker.Github, 32221, "[iOS] ScrollView does not resize when children are removed from StackLayout at runtime", PlatformAffected.iOS)]
+
+public class Issue32221 : ContentPage
+{
+ int labelCount = 3;
+ StackLayout labelStack;
+
+ public Issue32221()
+ {
+ // Create the label stack
+ labelStack = new StackLayout
+ {
+ BackgroundColor = Colors.Beige,
+ Spacing = 10
+ };
+
+ // Add initial labels
+ for (int i = 1; i <= labelCount; i++)
+ {
+ labelStack.Children.Add(new Label
+ {
+ Text = $"Label {i}",
+ FontSize = 18,
+ Padding = new Thickness(10)
+ });
+ }
+
+ // Create ScrollView to hold the label stack
+ var scrollView = new ScrollView
+ {
+ Content = labelStack
+ };
+
+ // Create buttons
+ var addButton = new Button
+ {
+ Text = "Add Label",
+ AutomationId = "AddLabelButton"
+ };
+ addButton.Clicked += OnAddLabelClicked;
+
+ var removeButton = new Button
+ {
+ Text = "Remove Label",
+ AutomationId = "RemoveLabelButton"
+ };
+ removeButton.Clicked += OnRemoveLabelClicked;
+
+ // Create the main layout
+ var mainLayout = new VerticalStackLayout
+ {
+ Padding = 20,
+ Children = { scrollView, addButton, removeButton }
+ };
+
+ // Set the page content
+ Content = mainLayout;
+ }
+
+ void OnAddLabelClicked(object sender, EventArgs e)
+ {
+ labelCount++;
+ labelStack.Children.Add(new Label
+ {
+ Text = $"Label {labelCount}",
+ FontSize = 18,
+ Padding = new Thickness(10)
+ });
+ }
+
+ void OnRemoveLabelClicked(object sender, EventArgs e)
+ {
+ if (labelStack.Children.Count > 0)
+ {
+ labelStack.Children.RemoveAt(labelStack.Children.Count - 1);
+ labelCount--;
+ }
+ }
+}
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue32271.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue32271.cs
new file mode 100644
index 000000000000..fb3f65a75439
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue32271.cs
@@ -0,0 +1,146 @@
+using System.ComponentModel;
+using System.Runtime.CompilerServices;
+
+namespace Maui.Controls.Sample.Issues;
+
+[Issue(IssueTracker.Github, 32271, "ScrollView with RTL FlowDirection and Horizontal Orientation scrolls in the wrong direction on iOS", PlatformAffected.iOS)]
+
+public class Issue32271 : ContentPage
+{
+ Issue32271ScrollViewViewModel _viewModel;
+
+ public Issue32271()
+ {
+ _viewModel = new Issue32271ScrollViewViewModel();
+ BindingContext = _viewModel;
+
+ var grid = new Grid
+ {
+ RowDefinitions =
+ {
+ new RowDefinition { Height = GridLength.Star },
+ new RowDefinition { Height = GridLength.Auto },
+ new RowDefinition { Height = GridLength.Auto }
+ }
+ };
+
+ var scrollViewContent = new ContentView
+ {
+ AutomationId = "ScrollViewContent"
+ };
+ scrollViewContent.SetBinding(ContentView.ContentProperty, static (Issue32271ScrollViewViewModel vm) => vm.Content);
+
+ var scrollView = new ScrollView
+ {
+ Content = scrollViewContent,
+ FlowDirection = FlowDirection.RightToLeft,
+ };
+ scrollView.SetBinding(ScrollView.OrientationProperty, static (Issue32271ScrollViewViewModel vm) => vm.Orientation);
+
+ grid.Add(scrollView);
+ Grid.SetRow(scrollView, 0);
+
+ var toggleButton = new Button
+ {
+ Text = "Toggle Orientation",
+ AutomationId = "ToggleOrientationButton"
+ };
+ toggleButton.Clicked += Button_Clicked;
+
+ var scrollToEndButton = new Button
+ {
+ Text = "Scroll",
+ AutomationId = "ScrollToEndButton"
+ };
+ scrollToEndButton.Clicked += async (s, e) =>
+ {
+ if (_viewModel.Content != null)
+ await scrollView.ScrollToAsync(_viewModel.Content, ScrollToPosition.Center, true);
+ };
+
+ var buttonLayout = new HorizontalStackLayout
+ {
+ HorizontalOptions = LayoutOptions.Center,
+ Spacing = 20,
+ Children = { toggleButton, scrollToEndButton }
+ };
+
+ grid.Add(buttonLayout);
+ Grid.SetRow(buttonLayout, 1);
+
+ Content = grid;
+ }
+
+ void Button_Clicked(object sender, EventArgs e)
+ {
+ if (_viewModel.Orientation == ScrollOrientation.Vertical)
+ _viewModel.Orientation = ScrollOrientation.Horizontal;
+ else
+ _viewModel.Orientation = ScrollOrientation.Vertical;
+ }
+}
+
+
+public class Issue32271ScrollViewViewModel : INotifyPropertyChanged
+{
+ string _contentText;
+ ScrollOrientation _orientation = ScrollOrientation.Vertical;
+ View _content;
+
+ public ScrollOrientation Orientation
+ {
+ get => _orientation;
+ set
+ {
+ if (_orientation != value)
+ {
+ _orientation = value;
+ OnPropertyChanged();
+ }
+ }
+ }
+
+ public Issue32271ScrollViewViewModel()
+ {
+ _contentText = string.Empty;
+ Content = new Label
+ {
+ Text = string.Join(Environment.NewLine, Enumerable.Range(1, 100).Select(i =>
+ $"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed euismod, urna eu tincidunt consectetur, nisi nisl aliquam enim, eget facilisis enim nisl nec elit . Sed euismod, urna eu tincidunt consectetur, nisi nisl aliquam enim Eget facilisis enim nisl nec elit Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae. Nullam ac erat at dui laoreet aliquet. Praesent euismod, justo at dictum facilisis, urna erat dictum enim. {i}")),
+ FontSize = 18,
+ Padding = 10
+ };
+ }
+
+ public string ContentText
+ {
+ get => _contentText;
+ set
+ {
+ if (_contentText != value)
+ {
+ _contentText = value;
+ Content = new Label { Text = _contentText };
+ OnPropertyChanged();
+ }
+ }
+ }
+
+ public View Content
+ {
+ get => _content;
+ set
+ {
+ if (_content != value)
+ {
+ _content = value;
+ OnPropertyChanged();
+ }
+ }
+ }
+
+ public event PropertyChangedEventHandler PropertyChanged;
+
+ protected void OnPropertyChanged([CallerMemberName] string propertyName = "") =>
+ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
+}
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue32275.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue32275.cs
new file mode 100644
index 000000000000..500b90d952b1
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue32275.cs
@@ -0,0 +1,6 @@
+namespace Maui.Controls.Sample.Issues;
+
+[Issue(IssueTracker.Github, 32275, "Shell Flyout SafeArea Rendering", PlatformAffected.iOS)]
+public class Issue32275 : ShellFlyoutContentBase
+{
+}
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue32435.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue32435.cs
new file mode 100644
index 000000000000..4acfa08427b5
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue32435.cs
@@ -0,0 +1,74 @@
+using Microsoft.Maui.Controls;
+using System.Collections.ObjectModel;
+
+namespace Maui.Controls.Sample.Issues;
+
+[Issue(IssueTracker.Github, 32435, "Rotating the Simulator causes the text on the collection view to disappear", PlatformAffected.iOS)]
+
+public class Issue32435 : ContentPage
+{
+ readonly ObservableCollection items = new();
+ readonly CollectionView2 collectionView;
+
+ public Issue32435()
+ {
+ var rootGrid = new Grid
+ {
+ Margin = 20,
+ RowDefinitions =
+ {
+ new RowDefinition { Height = GridLength.Auto },
+ new RowDefinition { Height = GridLength.Auto },
+ new RowDefinition { Height = GridLength.Star }
+ }
+ };
+
+ var topStack = new StackLayout();
+ topStack.Add(new Label { Text = "CollectionView text should appear after rotating the device", AutomationId = "InstructionLabel" });
+ rootGrid.Add(topStack);
+ Grid.SetRow(topStack, 0);
+
+ var addButton = new Button
+ {
+ Text = "Add",
+ AutomationId = "AddButton",
+ HorizontalOptions = LayoutOptions.Start,
+ VerticalOptions = LayoutOptions.Start
+ };
+ addButton.Clicked += ButtonAdd_Clicked;
+ rootGrid.Add(addButton);
+ Grid.SetRow(addButton, 1);
+
+ var innerGrid = new Grid
+ {
+ RowDefinitions =
+ {
+ new RowDefinition { Height = GridLength.Auto },
+ new RowDefinition { Height = GridLength.Star }
+ }
+ };
+
+ collectionView = new CollectionView2
+ {
+ BackgroundColor = Colors.LightSalmon,
+ HeightRequest = 50,
+ ItemsLayout = new GridItemsLayout(ItemsLayoutOrientation.Horizontal)
+ };
+
+ innerGrid.Add(collectionView);
+ Grid.SetRow(collectionView, 0);
+
+ rootGrid.Add(innerGrid);
+ Grid.SetRow(innerGrid, 2);
+
+ items.Add("item: " + items.Count);
+ collectionView.ItemsSource = items;
+
+ Content = rootGrid;
+ }
+
+ void ButtonAdd_Clicked(object sender, System.EventArgs e)
+ {
+ items.Add("item: " + items.Count);
+ }
+}
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue32586.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue32586.cs
index f80dda548b78..6b6c404c8a57 100644
--- a/src/Controls/tests/TestCases.HostApp/Issues/Issue32586.cs
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue32586.cs
@@ -39,6 +39,7 @@ public Issue32586()
{
Text = "Top Marker",
FontSize = 12,
+ HeightRequest = 50,
HorizontalOptions = LayoutOptions.Center,
AutomationId = "TopMarker"
};
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue32724.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue32724.cs
new file mode 100644
index 000000000000..914c955993f4
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue32724.cs
@@ -0,0 +1,231 @@
+using System.Collections.ObjectModel;
+
+namespace Maui.Controls.Sample.Issues;
+
+[Issue(IssueTracker.Github, 32724, "Applying Shadow property affects the properties in Visual Transform Matrix", PlatformAffected.iOS | PlatformAffected.macOS)]
+
+public class Issue32724 : ContentPage
+{
+ Border _border;
+ double _scale = 1;
+ double _scaleX = 1;
+ double _scaleY = 1;
+ double _translationX = 0;
+ double _translationY = 0;
+ double _rotation = 0;
+ double _rotationX = 0;
+ double _rotationY = 0;
+ double _anchorX = 0.5;
+ double _anchorY = 0.5;
+ bool _shadowApplied = false;
+
+ public Issue32724()
+ {
+ _border = new Border
+ {
+ BackgroundColor = Colors.Red,
+ HeightRequest = 120,
+ WidthRequest = 120,
+ HorizontalOptions = LayoutOptions.Center
+ };
+
+ var btnScale = CreateButton("Scale +", OnScaleClicked, "ScaleButton");
+ var btnScaleX = CreateButton("ScaleX", OnScaleXClicked, "ScaleXButton");
+ var btnScaleY = CreateButton("ScaleY", OnScaleYClicked, "ScaleYButton");
+ var btnTranslationX = CreateButton("TranslationX", OnTranslationXClicked, "TranslationXButton");
+ var btnTranslationY = CreateButton("TranslationY", OnTranslationYClicked, "TranslationYButton");
+ var btnRot = CreateButton("Rotation +", OnRotationClicked, "RotationButton");
+ var btnRotX = CreateButton("RotationX +", OnRotationXClicked, "RotationXButton");
+ var btnRotY = CreateButton("RotationY +", OnRotationYClicked, "RotationYButton");
+ var btnAnchorX = CreateButton("AnchorX +", OnAnchorXClicked, "AnchorXButton");
+ var btnAnchorY = CreateButton("AnchorY +", OnAnchorYClicked, "AnchorYButton");
+ var btnShadow = CreateButton("Toggle Shadow", OnToggleShadowClicked, "ToggleShadowButton");
+ var btnReset = CreateButton("Reset", OnResetClicked, "ResetButton");
+
+ var buttonsScrollView = new ScrollView
+ {
+ VerticalOptions = LayoutOptions.Center,
+ Content = new VerticalStackLayout
+ {
+ Padding = 20,
+ Spacing = 20,
+ Children =
+ {
+ new HorizontalStackLayout
+ {
+ Spacing = 10,
+ HorizontalOptions = LayoutOptions.Center,
+ Children = {btnScale,btnScaleX, btnScaleY }
+ },
+
+ new HorizontalStackLayout
+ {
+ Spacing = 10,
+ HorizontalOptions = LayoutOptions.Center,
+ Children = { btnTranslationX, btnTranslationY }
+ },
+
+ new HorizontalStackLayout
+ {
+ Spacing = 10,
+ HorizontalOptions = LayoutOptions.Center,
+ Children = {btnRot, btnRotX, btnRotY }
+ },
+
+ new HorizontalStackLayout
+ {
+ Spacing = 10,
+ HorizontalOptions = LayoutOptions.Center,
+ Children = { btnAnchorX, btnAnchorY }
+ },
+
+ new HorizontalStackLayout
+ {
+ Spacing = 10,
+ HorizontalOptions = LayoutOptions.Center,
+ Children = { btnShadow, btnReset }
+ }
+ }
+ }
+ };
+
+ var grid = new Grid
+ {
+ RowDefinitions =
+ {
+ new RowDefinition { Height = new GridLength(2, GridUnitType.Star) },
+ new RowDefinition { Height = GridLength.Auto }
+ }
+ };
+
+ Grid.SetRow(_border, 0);
+ Grid.SetRow(buttonsScrollView, 1);
+ grid.Children.Add(_border);
+ grid.Children.Add(buttonsScrollView);
+ Content = grid;
+ }
+
+ Button CreateButton(string text, EventHandler clicked, string automationId)
+ {
+ var button = new Button { Text = text, Padding = 10, AutomationId = automationId };
+ button.Clicked += clicked;
+ return button;
+ }
+
+ void OnScaleClicked(object sender, EventArgs e)
+ {
+ _scale += 0.5;
+ _border.Scale = _scale;
+ }
+
+ void OnScaleXClicked(object sender, EventArgs e)
+ {
+ _scaleX += 0.5;
+ _border.ScaleX = _scaleX;
+ }
+
+ void OnScaleYClicked(object sender, EventArgs e)
+ {
+ _scaleY += 0.5;
+ _border.ScaleY = _scaleY;
+ }
+
+ void OnTranslationXClicked(object sender, EventArgs e)
+ {
+ _translationX += 50;
+ _border.TranslationX = _translationX;
+ }
+
+ void OnTranslationYClicked(object sender, EventArgs e)
+ {
+ _translationY += 50;
+ _border.TranslationY = _translationY;
+ }
+
+ void OnRotationClicked(object sender, EventArgs e)
+ {
+ _rotation += 45;
+ if (_rotation >= 360)
+ _rotation = 0;
+ _border.Rotation = _rotation;
+ }
+
+ void OnRotationXClicked(object sender, EventArgs e)
+ {
+ _rotationX += 45;
+ if (_rotationX >= 360)
+ _rotationX = 0;
+ _border.RotationX = _rotationX;
+ }
+
+ void OnRotationYClicked(object sender, EventArgs e)
+ {
+ _rotationY += 45;
+ if (_rotationY >= 360)
+ _rotationY = 0;
+ _border.RotationY = _rotationY;
+ }
+
+ void OnAnchorXClicked(object sender, EventArgs e)
+ {
+ _anchorX += 0.25;
+ if (_anchorX > 1)
+ _anchorX = 0;
+ _border.AnchorX = _anchorX;
+ }
+
+ void OnAnchorYClicked(object sender, EventArgs e)
+ {
+ _anchorY += 0.25;
+ if (_anchorY > 1)
+ _anchorY = 0;
+ _border.AnchorY = _anchorY;
+ }
+
+ void OnToggleShadowClicked(object sender, EventArgs e)
+ {
+ if (_shadowApplied)
+ {
+ // Remove shadow completely
+ _border.Shadow = null; // reset to default
+ _shadowApplied = false;
+ }
+ else
+ {
+ _border.Shadow = new Shadow
+ {
+ Brush = Brush.Black,
+ Opacity = 0.8f,
+ Offset = new Point(10, 10)
+ };
+ _shadowApplied = true;
+ }
+ }
+
+ void OnResetClicked(object sender, EventArgs e)
+ {
+ _scale = 1;
+ _scaleX = 1;
+ _scaleY = 1;
+ _translationX = 0;
+ _translationY = 0;
+ _rotation = 0;
+ _rotationX = 0;
+ _rotationY = 0;
+ _anchorX = 0.5;
+ _anchorY = 0.5;
+ _shadowApplied = false;
+
+ _border.Scale = _scale;
+ _border.ScaleX = _scaleX;
+ _border.ScaleY = _scaleY;
+ _border.TranslationX = _translationX;
+ _border.TranslationY = _translationY;
+ _border.Rotation = _rotation;
+ _border.RotationX = _rotationX;
+ _border.RotationY = _rotationY;
+ _border.AnchorX = _anchorX;
+ _border.AnchorY = _anchorY;
+ _border.Shadow = null;
+ }
+}
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue32731.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue32731.cs
deleted file mode 100644
index 3c2bd5973d1e..000000000000
--- a/src/Controls/tests/TestCases.HostApp/Issues/Issue32731.cs
+++ /dev/null
@@ -1,235 +0,0 @@
-using System.Collections.ObjectModel;
-
-namespace Maui.Controls.Sample.Issues;
-
-[Issue(IssueTracker.Github, 32731, "Applying Shadow property affects the properties in Visual Transform Matrix", PlatformAffected.Android)]
-
-public class Issue32731 : ContentPage
-{
- Border _border;
- double _scale = 1;
- double _scaleX = 1;
- double _scaleY = 1;
- double _translationX = 0;
- double _translationY = 0;
- double _rotation = 0;
- double _rotationX = 0;
- double _rotationY = 0;
- double _anchorX = 0.5;
- double _anchorY = 0.5;
- bool _shadowApplied = false;
-
- public Issue32731()
- {
- _border = new Border
- {
- BackgroundColor = Colors.Red,
- HeightRequest = 120,
- WidthRequest = 120,
- HorizontalOptions = LayoutOptions.Center
- };
-
- var btnScale = CreateButton("Scale +", OnScaleClicked, "ScaleButton");
- var btnScaleX = CreateButton("ScaleX", OnScaleXClicked, "ScaleXButton");
- var btnScaleY = CreateButton("ScaleY", OnScaleYClicked, "ScaleYButton");
- var btnTranslationX = CreateButton("TranslationX", OnTranslationXClicked, "TranslationXButton");
- var btnTranslationY = CreateButton("TranslationY", OnTranslationYClicked, "TranslationYButton");
- var btnRot = CreateButton("Rotation +", OnRotationClicked, "RotationButton");
- var btnRotX = CreateButton("RotationX +", OnRotationXClicked, "RotationXButton");
- var btnRotY = CreateButton("RotationY +", OnRotationYClicked, "RotationYButton");
- var btnAnchorX = CreateButton("AnchorX +", OnAnchorXClicked, "AnchorXButton");
- var btnAnchorY = CreateButton("AnchorY +", OnAnchorYClicked, "AnchorYButton");
- var btnShadow = CreateButton("Toggle Shadow", OnToggleShadowClicked, "ToggleShadowButton");
- var btnReset = CreateButton("Reset", OnResetClicked, "ResetButton");
-
- var buttonsScrollView = new ScrollView
- {
- VerticalOptions = LayoutOptions.Center,
- Content = new VerticalStackLayout
- {
- Padding = 20,
- Spacing = 20,
- Children =
- {
- new HorizontalStackLayout
- {
- Spacing = 10,
- HorizontalOptions = LayoutOptions.Center,
- Children = {btnScale,btnScaleX, btnScaleY }
- },
-
- new HorizontalStackLayout
- {
- Spacing = 10,
- HorizontalOptions = LayoutOptions.Center,
- Children = { btnTranslationX, btnTranslationY }
- },
-
- new HorizontalStackLayout
- {
- Spacing = 10,
- HorizontalOptions = LayoutOptions.Center,
- Children = {btnRot, btnRotX, btnRotY }
- },
-
- new HorizontalStackLayout
- {
- Spacing = 10,
- HorizontalOptions = LayoutOptions.Center,
- Children = { btnAnchorX, btnAnchorY }
- },
-
- new HorizontalStackLayout
- {
- Spacing = 10,
- HorizontalOptions = LayoutOptions.Center,
- Children = { btnShadow, btnReset }
- }
- }
- }
- };
-
- var grid = new Grid
- {
- RowDefinitions =
- {
- new RowDefinition { Height = new GridLength(2, GridUnitType.Star) },
- new RowDefinition { Height = GridLength.Auto }
- }
- };
-
- Grid.SetRow(_border, 0);
- Grid.SetRow(buttonsScrollView, 1);
- grid.Children.Add(_border);
- grid.Children.Add(buttonsScrollView);
- Content = grid;
- }
-
- Button CreateButton(string text, EventHandler clicked, string automationId)
- {
- var button = new Button { Text = text, Padding = 10, AutomationId = automationId };
- button.Clicked += clicked;
- return button;
- }
-
- void OnScaleClicked(object sender, EventArgs e)
- {
- _scale += 0.5;
- _border.Scale = _scale;
- }
-
- void OnScaleXClicked(object sender, EventArgs e)
- {
- _scaleX += 0.5;
- _border.ScaleX = _scaleX;
- }
-
- void OnScaleYClicked(object sender, EventArgs e)
- {
- _scaleY += 0.5;
- _border.ScaleY = _scaleY;
- }
-
- void OnTranslationXClicked(object sender, EventArgs e)
- {
- _translationX += 50;
- _border.TranslationX = _translationX;
- }
-
- void OnTranslationYClicked(object sender, EventArgs e)
- {
- _translationY += 50;
- _border.TranslationY = _translationY;
- }
-
- void OnRotationClicked(object sender, EventArgs e)
- {
- _rotation += 45;
- if (_rotation >= 360)
- _rotation = 0;
- _border.Rotation = _rotation;
- }
-
- void OnRotationXClicked(object sender, EventArgs e)
- {
- _rotationX += 45;
- if (_rotationX >= 360)
- _rotationX = 0;
- _border.RotationX = _rotationX;
- }
-
- void OnRotationYClicked(object sender, EventArgs e)
- {
- _rotationY += 45;
- if (_rotationY >= 360)
- _rotationY = 0;
- _border.RotationY = _rotationY;
- }
-
- void OnAnchorXClicked(object sender, EventArgs e)
- {
- _anchorX += 0.25;
- if (_anchorX > 1)
- _anchorX = 0;
- _border.AnchorX = _anchorX;
- }
-
- void OnAnchorYClicked(object sender, EventArgs e)
- {
- _anchorY += 0.25;
- if (_anchorY > 1)
- _anchorY = 0;
- _border.AnchorY = _anchorY;
- }
-
- void OnToggleShadowClicked(object sender, EventArgs e)
- {
- if (_shadowApplied)
- {
- _border.Shadow = null;
- _shadowApplied = false;
- }
- else
- {
- _border.Shadow = new Shadow
- {
- Brush = Brush.Black,
- Opacity = 0.8f,
- Offset = new Point(10, 10)
- };
- _shadowApplied = true;
- }
- }
-
- void OnResetClicked(object sender, EventArgs e)
- {
- _scale = 1;
- _scaleX = 1;
- _scaleY = 1;
- _translationX = 0;
- _translationY = 0;
- _rotation = 0;
- _rotationX = 0;
- _rotationY = 0;
- _anchorX = 0.5;
- _anchorY = 0.5;
- _shadowApplied = false;
-
- _border.Scale = _scale;
- _border.ScaleX = _scaleX;
- _border.ScaleY = _scaleY;
- _border.TranslationX = _translationX;
- _border.TranslationY = _translationY;
- _border.Rotation = _rotation;
- _border.RotationX = _rotationX;
- _border.RotationY = _rotationY;
- _border.AnchorX = _anchorX;
- _border.AnchorY = _anchorY;
- _border.Shadow = new Shadow
- {
- Brush = Brush.Transparent,
- Opacity = 0f,
- Offset = new Point(0, 0)
- };
- }
-}
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue33110.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue33110.cs
new file mode 100644
index 000000000000..934cb764eaba
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue33110.cs
@@ -0,0 +1,66 @@
+namespace Maui.Controls.Sample.Issues;
+
+[Issue(IssueTracker.Github, 33110, "GraphicsView dirtyRect dimensions should be integers, not fractional values", PlatformAffected.UWP | PlatformAffected.Android)]
+public class Issue33110 : ContentPage
+{
+ public Issue33110()
+ {
+ Label descriptionLabel = new Label
+ {
+ Text = "The GraphicsView below has a fixed size of 100x50. Tap 'Check' to verify that the dirtyRect dimensions passed to Draw are integer values.",
+ };
+
+ Label resultLabel = new Label
+ {
+ Text = "Pending",
+ AutomationId = "ResultLabel"
+ };
+
+ Issue33110Drawable drawable = new Issue33110Drawable();
+
+ GraphicsView graphicsView = new GraphicsView
+ {
+ Drawable = drawable,
+ WidthRequest = 100,
+ HeightRequest = 50,
+ HorizontalOptions = LayoutOptions.Start,
+ };
+
+ Button checkButton = new Button
+ {
+ Text = "Check",
+ AutomationId = "CheckButton",
+ HorizontalOptions = LayoutOptions.Start,
+ };
+
+ checkButton.Clicked += (s, e) =>
+ resultLabel.Text = drawable.HasIntegerDimensions ? "Pass" : "Fail";
+
+ Content = new VerticalStackLayout
+ {
+ Padding = new Thickness(20),
+ Spacing = 12,
+ Children =
+ {
+ descriptionLabel,
+ graphicsView,
+ checkButton,
+ resultLabel
+ }
+ };
+ }
+}
+
+class Issue33110Drawable : IDrawable
+{
+ public bool HasIntegerDimensions { get; set; }
+
+ public void Draw(ICanvas canvas, RectF dirtyRect)
+ {
+ canvas.FillColor = Colors.Blue;
+ canvas.FillRectangle(dirtyRect);
+
+ HasIntegerDimensions = Math.Abs(dirtyRect.Width - MathF.Round(dirtyRect.Width)) < 0.01f
+ && Math.Abs(dirtyRect.Height - MathF.Round(dirtyRect.Height)) < 0.01f;
+ }
+}
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue33307.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue33307.cs
new file mode 100644
index 000000000000..b4e93d3471c2
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue33307.cs
@@ -0,0 +1,533 @@
+using System.Collections.ObjectModel;
+using System.ComponentModel;
+
+namespace Maui.Controls.Sample.Issues;
+
+[Issue(IssueTracker.Github, 33307, "The Picker is still binding to the property and reacts to data changes after the page is closed.", PlatformAffected.UWP)]
+
+public class Issue33307 : TestNavigationPage
+{
+ protected override void Init()
+ {
+ Navigation.PushAsync(new Issue33307ContentPage());
+ }
+}
+
+public class Issue33307ContentPage : ContentPage
+{
+ Issue33307ClassA mainData;
+
+ public Issue33307ContentPage()
+ {
+ mainData = new Issue33307ClassA();
+
+ var stackLayout = new HorizontalStackLayout
+ {
+ Spacing = 100,
+ HorizontalOptions = LayoutOptions.Center,
+ VerticalOptions = LayoutOptions.Center
+ };
+
+ var buttonPage1 = new Button
+ {
+ Text = "Page1",
+ AutomationId = "Page1",
+ HeightRequest = 180,
+ WidthRequest = 180
+ };
+ buttonPage1.Clicked += ButtonPage1_Clicked;
+
+ var buttonPage2 = new Button
+ {
+ Text = "Page2",
+ AutomationId = "Page2",
+ HeightRequest = 180,
+ WidthRequest = 180
+ };
+ buttonPage2.Clicked += ButtonPage2_Clicked;
+
+ stackLayout.Add(buttonPage1);
+ stackLayout.Add(buttonPage2);
+
+ Content = new Grid
+ {
+ Children = { stackLayout }
+ };
+ }
+
+ async void ButtonPage1_Clicked(object sender, EventArgs e)
+ {
+ await Navigation.PushAsync(new Issue33307Page1(mainData));
+ }
+
+ async void ButtonPage2_Clicked(object sender, EventArgs e)
+ {
+ await Navigation.PushAsync(new Issue33307Page2(mainData));
+ }
+}
+
+public class Issue33307ClassA
+{
+ public int id_counter { get; set; }
+ public ObservableCollection itemsB { get; set; } = new();
+ public ObservableCollection itemsC { get; set; } = new();
+}
+
+public class Issue33307ClassB
+{
+ public int id { get; set; }
+ public string name { get; set; } = string.Empty;
+ public bool isSelected { get; set; }
+
+ public object Clone()
+ {
+ return MemberwiseClone();
+ }
+}
+
+public class Issue33307ClassC : INotifyPropertyChanged
+{
+ public int id { get; set; }
+ public string name { get; set; } = string.Empty;
+ public ObservableCollection itemsB { get; set; } = new();
+
+ public event PropertyChangedEventHandler PropertyChanged;
+
+ public Issue33307ClassB selected_item
+ {
+ get
+ {
+ for (var i = 0; i < itemsB.Count; i++)
+ {
+ if (itemsB[i].isSelected)
+ return itemsB[i];
+ }
+ return null;
+ }
+ set
+ {
+ if (value == null)
+ {
+ for (var i = 0; i < itemsB.Count; i++)
+ itemsB[i].isSelected = false;
+ }
+ else
+ {
+ for (var i = 0; i < itemsB.Count; i++)
+ {
+ if (itemsB[i].name == value.name)
+ {
+ itemsB[i].isSelected = true;
+ }
+ else
+ {
+ itemsB[i].isSelected = false;
+ }
+ }
+ }
+ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(selected_item)));
+ }
+ }
+}
+
+public class Issue33307Page1 : ContentPage
+{
+ Issue33307ClassA mainData;
+ CollectionView collectionView1;
+
+ public Issue33307Page1(Issue33307ClassA mainData)
+ {
+ this.mainData = mainData;
+ Title = "Page1";
+
+ var headerGrid = new Grid
+ {
+ ColumnDefinitions =
+ {
+ new ColumnDefinition(GridLength.Star),
+ new ColumnDefinition(new GridLength(60))
+ },
+ ColumnSpacing = 5
+ };
+
+ var nameLabel = new Label { Text = "Name", VerticalOptions = LayoutOptions.Center };
+ Grid.SetColumn(nameLabel, 0);
+ headerGrid.Add(nameLabel);
+
+ var deleteLabel = new Label { Text = "Delete", VerticalOptions = LayoutOptions.Center };
+ Grid.SetColumn(deleteLabel, 1);
+ headerGrid.Add(deleteLabel);
+
+ var headerLayout = new VerticalStackLayout
+ {
+ Spacing = 10,
+ VerticalOptions = LayoutOptions.End,
+ Children =
+ {
+ headerGrid,
+ new BoxView { Color = Colors.Black, HeightRequest = 2, CornerRadius = 20 }
+ }
+ };
+
+ collectionView1 = new CollectionView
+ {
+ HorizontalOptions = LayoutOptions.Fill,
+ BackgroundColor = Colors.WhiteSmoke,
+ ItemTemplate = new DataTemplate(() =>
+ {
+ var itemGrid = new Grid
+ {
+ ColumnSpacing = 5,
+ Margin = new Thickness(0, 8, 0, 8),
+ ColumnDefinitions =
+ {
+ new ColumnDefinition(GridLength.Star),
+ new ColumnDefinition(new GridLength(60))
+ }
+ };
+
+ var entry = new Entry
+ {
+ Placeholder = "Name",
+ VerticalOptions = LayoutOptions.Center
+ };
+ entry.SetBinding(Entry.TextProperty, "name");
+ Grid.SetColumn(entry, 0);
+ itemGrid.Add(entry);
+
+ return new VerticalStackLayout
+ {
+ Margin = new Thickness(1, 0, 1, 0),
+ Children =
+ {
+ itemGrid,
+ new BoxView { Color = Colors.Black, HeightRequest = 2, CornerRadius = 20 }
+ }
+ };
+ }),
+ EmptyView = new Label
+ {
+ Text = "No items to display",
+ HorizontalTextAlignment = TextAlignment.Center,
+ VerticalTextAlignment = TextAlignment.Center
+ }
+ };
+ collectionView1.ItemsSource = this.mainData.itemsB;
+
+ var addButton = new Button
+ {
+ Text = "Add items",
+ AutomationId = "AddItems",
+ HorizontalOptions = LayoutOptions.Start
+ };
+ addButton.Clicked += ButtonAddRow_Clicked;
+
+ var delButton = new Button
+ {
+ Text = "Delete",
+ AutomationId = "DeleteItem"
+ };
+ delButton.Clicked += ButtonDeleteRow_Clicked;
+
+ var hStack = new HorizontalStackLayout
+ {
+ Spacing = 20,
+ Children =
+ {
+ addButton,
+ delButton
+ }
+ };
+
+ var mainGrid = new Grid
+ {
+ RowDefinitions =
+ {
+ new RowDefinition(GridLength.Auto),
+ new RowDefinition(GridLength.Star),
+ new RowDefinition(new GridLength(50))
+ },
+ RowSpacing = 11,
+ Margin = new Thickness(10, 10, 10, 20)
+ };
+
+ Grid.SetRow(headerLayout, 0);
+ mainGrid.Add(headerLayout);
+
+ Grid.SetRow(collectionView1, 1);
+ mainGrid.Add(collectionView1);
+
+ Grid.SetRow(hStack, 2);
+ mainGrid.Add(hStack);
+
+ Content = mainGrid;
+ }
+
+ void ButtonAddRow_Clicked(object sender, EventArgs e)
+ {
+ var itemB1 = new Issue33307ClassB
+ {
+ id = mainData.id_counter++,
+ name = "itemB1"
+ };
+ mainData.itemsB.Add(itemB1);
+
+ var itemB2 = new Issue33307ClassB
+ {
+ id = mainData.id_counter++,
+ name = "itemB2"
+ };
+ mainData.itemsB.Add(itemB2);
+
+ var itemB3 = new Issue33307ClassB
+ {
+ id = mainData.id_counter++,
+ name = "itemB3"
+ };
+ mainData.itemsB.Add(itemB3);
+ }
+
+ async void ButtonDeleteRow_Clicked(object sender, EventArgs e)
+ {
+ if (mainData.itemsB.Count > 1)
+ {
+ var item = mainData.itemsB[1];
+ RemoveInDependencies(item.id);
+ mainData.itemsB.Remove(item);
+ }
+ }
+
+ void RemoveInDependencies(int id)
+ {
+ foreach (var itemC in mainData.itemsC)
+ {
+ for (int i = 0; i < itemC.itemsB.Count; i++)
+ {
+ if (itemC.itemsB[i].id == id)
+ {
+ itemC.itemsB.RemoveAt(i);
+ break;
+ }
+ }
+ }
+ }
+}
+
+public class Issue33307Page2 : ContentPage
+{
+ Issue33307ClassA mainData;
+ CollectionView collectionView1;
+ Label selectedItemLabel;
+
+ public Issue33307Page2(Issue33307ClassA mainData)
+ {
+ this.mainData = mainData;
+ Title = "Page2";
+
+ var headerGrid = new Grid
+ {
+ ColumnDefinitions =
+ {
+ new ColumnDefinition(GridLength.Star),
+ new ColumnDefinition(GridLength.Star),
+ new ColumnDefinition(new GridLength(60))
+ },
+ ColumnSpacing = 5
+ };
+
+ var nameLabel = new Label
+ {
+ Text = "Name",
+ VerticalOptions = LayoutOptions.Center,
+ HorizontalOptions = LayoutOptions.Center
+ };
+ Grid.SetColumn(nameLabel, 0);
+ headerGrid.Add(nameLabel);
+
+ var itemBLabel = new Label
+ {
+ Text = "ItemB",
+ VerticalOptions = LayoutOptions.Center,
+ HorizontalOptions = LayoutOptions.Center
+ };
+ Grid.SetColumn(itemBLabel, 1);
+ headerGrid.Add(itemBLabel);
+
+ var deleteLabel = new Label
+ {
+ Text = "Delete",
+ VerticalOptions = LayoutOptions.Center,
+ HorizontalOptions = LayoutOptions.Center
+ };
+ Grid.SetColumn(deleteLabel, 2);
+ headerGrid.Add(deleteLabel);
+
+ var headerLayout = new VerticalStackLayout
+ {
+ Spacing = 10,
+ VerticalOptions = LayoutOptions.End,
+ Children =
+ {
+ headerGrid,
+ new BoxView { Color = Colors.Black, HeightRequest = 2, CornerRadius = 20 }
+ }
+ };
+
+ collectionView1 = new CollectionView
+ {
+ HorizontalOptions = LayoutOptions.Fill,
+ BackgroundColor = Colors.WhiteSmoke,
+ ItemTemplate = new DataTemplate(() =>
+ {
+ var itemGrid = new Grid
+ {
+ ColumnSpacing = 5,
+ ColumnDefinitions =
+ {
+ new ColumnDefinition(GridLength.Star),
+ new ColumnDefinition(GridLength.Star),
+ new ColumnDefinition(new GridLength(60))
+ }
+ };
+
+ var entry = new Entry
+ {
+ Placeholder = "Name",
+ VerticalOptions = LayoutOptions.Center
+ };
+ entry.SetBinding(Entry.TextProperty, "name");
+ Grid.SetColumn(entry, 0);
+ itemGrid.Add(entry);
+
+ var rowPicker = new Picker
+ {
+ TextColor = Colors.Black,
+ TitleColor = Colors.Gray
+ };
+ rowPicker.SetBinding(Picker.ItemsSourceProperty, "itemsB");
+ rowPicker.ItemDisplayBinding = new Binding("name");
+ rowPicker.SetBinding(Picker.SelectedItemProperty, "selected_item");
+ rowPicker.SelectedIndexChanged += (s, e) =>
+ {
+ if (rowPicker.SelectedItem is Issue33307ClassB selectedItem)
+ {
+ if (selectedItemLabel != null)
+ selectedItemLabel.Text = selectedItem.name.ToString();
+ }
+ else
+ {
+ selectedItemLabel?.Text = "None";
+ }
+ };
+ Grid.SetColumn(rowPicker, 1);
+ itemGrid.Add(rowPicker);
+
+ return new VerticalStackLayout
+ {
+ Margin = new Thickness(1, 0, 1, 0),
+ Children =
+ {
+ itemGrid,
+ new BoxView { Color = Colors.Black, HeightRequest = 2, CornerRadius = 20 }
+ }
+ };
+ }),
+ EmptyView = new Label
+ {
+ Text = "No items to display",
+ HorizontalTextAlignment = TextAlignment.Center,
+ VerticalTextAlignment = TextAlignment.Center
+ }
+ };
+ collectionView1.ItemsSource = this.mainData.itemsC;
+
+ var addButton = new Button
+ {
+ Text = "Add",
+ AutomationId = "Add",
+ HorizontalOptions = LayoutOptions.Start
+ };
+ addButton.Clicked += ButtonAddRow_Clicked;
+
+ var delItem = new Button
+ {
+ Text = "Select Second Item",
+ AutomationId = "SelectSecondItem",
+ HorizontalOptions = LayoutOptions.Start
+ };
+ delItem.Clicked += ButtonDeleteRow_Clicked;
+
+ selectedItemLabel = new Label
+ {
+ Text = "None",
+ AutomationId = "StatusLabel",
+ VerticalOptions = LayoutOptions.Center,
+ TextColor = Colors.DarkBlue,
+ FontAttributes = FontAttributes.Bold
+ };
+
+ var hStack = new HorizontalStackLayout
+ {
+ Spacing = 20,
+ Children =
+ {
+ addButton,
+ delItem,
+ selectedItemLabel
+ }
+ };
+
+ var mainGrid = new Grid
+ {
+ RowDefinitions =
+ {
+ new RowDefinition(GridLength.Auto),
+ new RowDefinition(GridLength.Star),
+ new RowDefinition(new GridLength(50))
+ },
+ RowSpacing = 11,
+ Margin = new Thickness(10, 10, 10, 20)
+ };
+
+ Grid.SetRow(headerLayout, 0);
+ mainGrid.Add(headerLayout);
+
+ Grid.SetRow(collectionView1, 1);
+ mainGrid.Add(collectionView1);
+
+ Grid.SetRow(hStack, 2);
+ mainGrid.Add(hStack);
+
+ Content = mainGrid;
+ }
+
+ async void ButtonDeleteRow_Clicked(object sender, EventArgs e)
+ {
+ foreach (var itemC in mainData.itemsC)
+ {
+ if (itemC.itemsB.Count > 1)
+ {
+ itemC.selected_item = itemC.itemsB[1];
+ }
+ else if (itemC.itemsB.Count > 0)
+ {
+ itemC.selected_item = itemC.itemsB[0];
+ }
+ }
+ }
+
+ void ButtonAddRow_Clicked(object sender, EventArgs e)
+ {
+ var itemC = new Issue33307ClassC
+ {
+ id = mainData.id_counter++
+ };
+
+ foreach (var itemB in mainData.itemsB)
+ {
+ itemC.itemsB.Add((Issue33307ClassB)itemB.Clone());
+ }
+
+ mainData.itemsC.Add(itemC);
+ }
+}
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue33785.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue33785.cs
new file mode 100644
index 000000000000..de63c2ad835d
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue33785.cs
@@ -0,0 +1,76 @@
+using Microsoft.Maui.Controls.PlatformConfiguration;
+using Microsoft.Maui.Controls.PlatformConfiguration.WindowsSpecific;
+
+namespace Maui.Controls.Sample.Issues;
+
+[Issue(IssueTracker.Github, 33785, "[Windows] FlyoutPage CollapsedPaneWidth Not Working", PlatformAffected.UWP)]
+public class Issue33785 : TestFlyoutPage
+{
+ Microsoft.Maui.Controls.Label _label;
+ protected override void Init()
+ {
+ this.On().SetCollapseStyle(CollapseStyle.Partial);
+ this.On().CollapsedPaneWidth(50);
+
+ // Set the flyout page properties
+ FlyoutLayoutBehavior = FlyoutLayoutBehavior.Popover;
+
+ // Create the flyout content
+ var flyoutPage = new ContentPage
+ {
+ Title = "Master",
+ BackgroundColor = Colors.Blue
+ };
+
+ var page1Button = new Button
+ {
+ Text = "Change",
+ AutomationId = "FlyoutItem",
+ HorizontalOptions = LayoutOptions.Start,
+ VerticalOptions = LayoutOptions.Center
+ };
+ page1Button.Clicked += (s, e) =>
+ {
+ this.On().CollapsedPaneWidth(100);
+ _label.Text = "CollapsedPaneWidth set to 100";
+ };
+
+ flyoutPage.Content = new VerticalStackLayout
+ {
+ Children = { page1Button }
+ };
+
+ // Create the detail content
+ var detailPage = new ContentPage
+ {
+ Title = "Detail",
+ BackgroundColor = Colors.LightYellow
+ };
+
+ _label = new Microsoft.Maui.Controls.Label
+ {
+ Text = "Test for CollapsedPaneWidth",
+ AutomationId = "CollapsedPaneLabel",
+ HorizontalOptions = LayoutOptions.Center,
+ HorizontalTextAlignment = TextAlignment.Center,
+ };
+
+ detailPage.Content = new VerticalStackLayout
+ {
+ Children = {
+ new Microsoft.Maui.Controls.Label
+ {
+ Text = "Welcome to .NET MAUI!",
+ TextColor = Colors.Black,
+ HorizontalOptions = LayoutOptions.Center,
+ VerticalOptions = LayoutOptions.Center
+ },
+ _label
+ }
+ };
+
+ // Set the flyout and detail pages
+ Flyout = flyoutPage;
+ Detail = detailPage;
+ }
+}
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue34257.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue34257.cs
deleted file mode 100644
index fd62e7d944f8..000000000000
--- a/src/Controls/tests/TestCases.HostApp/Issues/Issue34257.cs
+++ /dev/null
@@ -1,166 +0,0 @@
-using System.Collections.ObjectModel;
-
-namespace Maui.Controls.Sample.Issues;
-
-[Issue(IssueTracker.Github, 34257, "CollectionView vertical grid item spacing updates all rows and columns", PlatformAffected.Android | PlatformAffected.iOS | PlatformAffected.macOS)]
-public class Issue34257 : ContentPage
-{
- readonly GridItemsLayout _itemsLayout;
- readonly Label _statusLabel;
-
- public Issue34257()
- {
- _itemsLayout = new GridItemsLayout(2, ItemsLayoutOrientation.Vertical)
- {
- HorizontalItemSpacing = 0,
- VerticalItemSpacing = 0
- };
-
- _statusLabel = new Label
- {
- AutomationId = "StatusLabel",
- Text = "Spacing=0,0"
- };
-
- var applySpacingButton = new Button
- {
- AutomationId = "ApplyHorizontalSpacingButton",
- Text = "Apply horizontal spacing"
- };
- applySpacingButton.Clicked += OnApplyHorizontalSpacingClicked;
-
- var applyVerticalSpacingButton = new Button
- {
- AutomationId = "ApplyVerticalSpacingButton",
- Text = "Apply vertical spacing"
- };
- applyVerticalSpacingButton.Clicked += OnApplyVerticalSpacingClicked;
-
- var collectionView = new CollectionView
- {
- AutomationId = "TestCollectionView",
- HeightRequest = 260,
- HorizontalOptions = LayoutOptions.Center,
- ItemsLayout = _itemsLayout,
- ItemsSource = CreateItems(),
- ItemSizingStrategy = ItemSizingStrategy.MeasureAllItems,
- SelectionMode = SelectionMode.None,
- WidthRequest = 340
- };
- collectionView.ItemTemplate = new DataTemplate(() =>
- {
- var titleLabel = new Label
- {
- FontAttributes = FontAttributes.Bold,
- LineBreakMode = LineBreakMode.TailTruncation
- };
- titleLabel.SetBinding(Label.TextProperty, nameof(SpacingIssueItem.Name));
-
- var locationLabel = new Label
- {
- FontAttributes = FontAttributes.Italic,
- LineBreakMode = LineBreakMode.TailTruncation,
- VerticalOptions = LayoutOptions.End
- };
- locationLabel.SetBinding(Label.TextProperty, nameof(SpacingIssueItem.Location));
-
- var textLayout = new Grid
- {
- RowDefinitions =
- {
- new RowDefinition { Height = GridLength.Auto },
- new RowDefinition { Height = GridLength.Auto }
- }
- };
- textLayout.Add(titleLabel);
- textLayout.Add(locationLabel, 0, 1);
-
- var root = new Grid
- {
- ColumnDefinitions =
- {
- new ColumnDefinition { Width = 70 },
- new ColumnDefinition { Width = GridLength.Star }
- },
- Padding = 10
- };
- root.SetBinding(AutomationIdProperty, nameof(SpacingIssueItem.AutomationId));
- root.SetBinding(BackgroundColorProperty, nameof(SpacingIssueItem.BackgroundColor));
-
- var imagePlaceholder = new Border
- {
- Background = Colors.DarkSlateBlue,
- HeightRequest = 60,
- StrokeThickness = 0,
- VerticalOptions = LayoutOptions.Center,
- WidthRequest = 60
- };
-
- root.Add(imagePlaceholder);
- root.Add(textLayout, 1, 0);
-
- return root;
- });
-
- Content = new ScrollView
- {
- Content = new VerticalStackLayout
- {
- Padding = 20,
- Spacing = 12,
- Children =
- {
- new Label { Text = "Issue 34257 reproduces a spacing update bug in a two-column vertical CollectionView grid." },
- new Label { Text = "Apply horizontal or vertical spacing and verify both columns and rows resize consistently." },
- new HorizontalStackLayout
- {
- Spacing = 12,
- Children =
- {
- applySpacingButton,
- applyVerticalSpacingButton
- }
- },
- _statusLabel,
- collectionView
- }
- }
- };
- }
-
- void OnApplyHorizontalSpacingClicked(object sender, EventArgs e)
- {
- _itemsLayout.VerticalItemSpacing = 0;
- _itemsLayout.HorizontalItemSpacing = 80;
- _statusLabel.Text = "Spacing=0,80";
- }
-
- void OnApplyVerticalSpacingClicked(object sender, EventArgs e)
- {
- _itemsLayout.VerticalItemSpacing = 40;
- _itemsLayout.HorizontalItemSpacing = 0;
- _statusLabel.Text = "Spacing=40,0";
- }
-
- static ObservableCollection CreateItems()
- {
- return
- [
- new SpacingIssueItem("FirstColumnTopItem", "Capuchin", "Central America", Colors.LightSkyBlue),
- new SpacingIssueItem("SecondColumnTopItem", "Spider", "South America", Colors.LightSalmon),
- new SpacingIssueItem("FirstColumnBottomItem", "Howler", "South America", Colors.PaleGreen),
- new SpacingIssueItem("SecondColumnBottomItem", "Baboon", "Africa", Colors.Khaki)
- ];
- }
-
- class SpacingIssueItem(string automationId, string name, string location, Color backgroundColor)
- {
- public string AutomationId { get; } = automationId;
-
- public Color BackgroundColor { get; } = backgroundColor;
-
- public string Location { get; } = location;
-
- public string Name { get; } = name;
- }
-}
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue34318.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue34318.cs
new file mode 100644
index 000000000000..567e57f953c0
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue34318.cs
@@ -0,0 +1,117 @@
+using Microsoft.Maui.ApplicationModel;
+using Microsoft.Maui.Controls;
+using Microsoft.Maui.Controls.CustomAttributes;
+
+namespace Maui.Controls.Sample.Issues;
+
+[Issue(IssueTracker.Github, 34318, "Shell Navigating event should fire on ShellContent change", PlatformAffected.All)]
+public class Issue34318 : Shell
+{
+ Label labelA;
+ Label labelB;
+ Label navigatingCountLabel;
+ int navigatingCount;
+
+ public Issue34318()
+ {
+ labelA = new Label
+ {
+ Text = "Waiting",
+ AutomationId = "ResultLabelA"
+ };
+
+ labelB = new Label
+ {
+ Text = "Waiting",
+ AutomationId = "ResultLabelB"
+ };
+
+ navigatingCountLabel = new Label
+ {
+ Text = "0",
+ AutomationId = "NavigatingCountLabel"
+ };
+
+ var section = new ShellSection();
+
+ var pageA = new Issue34318_PageA(labelA);
+
+ var contentA = new ShellContent
+ {
+ Content = pageA
+ };
+
+ var contentB = new ShellContent
+ {
+ Content = new ContentPage
+ {
+ Title = "PageB",
+ Content = new VerticalStackLayout
+ {
+ Children =
+ {
+ new Label
+ {
+ Text = "Page B",
+ AutomationId = "PageBLabel"
+ },
+ labelB,
+ navigatingCountLabel
+ }
+ }
+ }
+ };
+
+ section.Items.Add(contentA);
+ section.Items.Add(contentB);
+
+ var item = new ShellItem();
+ item.Items.Add(section);
+
+ Items.Add(item);
+
+ Navigating += (_, __) =>
+ {
+ MainThread.BeginInvokeOnMainThread(() =>
+ {
+ navigatingCount++;
+
+ labelA.Text = "Navigating";
+ labelB.Text = "Navigating";
+ navigatingCountLabel.Text = navigatingCount.ToString();
+ });
+ };
+ }
+
+ public class Issue34318_PageA : ContentPage
+ {
+ public Issue34318_PageA(Label label)
+ {
+ Title = "PageA";
+
+ var button = new Button
+ {
+ Text = "Change Content",
+ AutomationId = "ChangeContentButton"
+ };
+
+ button.Clicked += (s, e) =>
+ {
+ Element parent = this;
+
+ while (parent != null && parent is not ShellSection)
+ parent = parent.Parent;
+
+ if (parent is ShellSection section && section.Items.Count > 1)
+ {
+ section.CurrentItem = section.Items[1];
+ }
+ };
+
+ Content = new VerticalStackLayout
+ {
+ Children = { button, label }
+ };
+ }
+ }
+}
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue34422.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue34422.cs
new file mode 100644
index 000000000000..63fb9e2df749
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue34422.cs
@@ -0,0 +1,50 @@
+namespace Maui.Controls.Sample.Issues;
+
+[Issue(IssueTracker.Github, 34422, "SearchBar clear button still appears on MacCatalyst after clearing input", PlatformAffected.macOS)]
+public class Issue34422 : ContentPage
+{
+ readonly SearchBar _searchBar;
+
+ public Issue34422()
+ {
+ _searchBar = new SearchBar
+ {
+ Placeholder = "Search...",
+ AutomationId = "TestSearchBar"
+ };
+
+ var addTextButton = new Button
+ {
+ Text = "Add Text",
+ AutomationId = "AddTextButton"
+ };
+
+ addTextButton.Clicked += (s, e) =>
+ {
+ _searchBar.Text = "Search text";
+ };
+
+ var clearButton = new Button
+ {
+ Text = "Clear SearchBar Text",
+ AutomationId = "ClearButton"
+ };
+
+ clearButton.Clicked += (s, e) =>
+ {
+ _searchBar.Text = string.Empty;
+ };
+
+ Content = new VerticalStackLayout
+ {
+ Padding = new Thickness(20),
+ Spacing = 10,
+ Children =
+ {
+ _searchBar,
+ addTextButton,
+ clearButton
+ }
+ };
+ }
+}
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue34666.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue34666.cs
index 463cfbe06172..f6cb4c5bfbca 100644
--- a/src/Controls/tests/TestCases.HostApp/Issues/Issue34666.cs
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue34666.cs
@@ -1,6 +1,6 @@
namespace Maui.Controls.Sample.Issues
{
- [Issue(IssueTracker.Github, 34666, "The C6 page cannot scroll on Windows and Android platforms", PlatformAffected.All)]
+ [Issue(IssueTracker.Github, 34666, "Disabling RefreshView cascades IsEnabled=false to its child CollectionView, preventing scrolling", PlatformAffected.All)]
public class Issue34666 : ContentPage
{
public Issue34666()
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue34931.xaml b/src/Controls/tests/TestCases.HostApp/Issues/Issue34931.xaml
new file mode 100644
index 000000000000..2a03b8325872
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue34931.xaml
@@ -0,0 +1,70 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue34931.xaml.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue34931.xaml.cs
new file mode 100644
index 000000000000..1fd415759f8e
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue34931.xaml.cs
@@ -0,0 +1,150 @@
+namespace Maui.Controls.Sample.Issues;
+
+[Issue(IssueTracker.Github, 34931, "Shell flyout item template does not update selected visuals after DynamicResource changes", PlatformAffected.All)]
+public partial class Issue34931 : TestShell
+{
+ internal const string InitialPrimaryColor = "#512BD4";
+ internal const string UpdatedPrimaryColor = "#FF6347";
+
+ public Issue34931()
+ {
+ if (Application.Current is not null)
+ Application.Current.Resources["Primary"] = Color.FromArgb(InitialPrimaryColor);
+
+ InitializeComponent();
+ IncreaseFlyoutItemsHeightSoUITestsCanClickOnThem();
+ }
+
+ protected override void Init()
+ {
+ }
+}
+
+public class Issue34931MainPage : ContentPage
+{
+ readonly Label _currentColorLabel;
+ readonly string[] _colors = [Issue34931.InitialPrimaryColor, Issue34931.UpdatedPrimaryColor];
+ int _colorIndex;
+
+ public Issue34931MainPage()
+ {
+ Title = "Home";
+
+ _currentColorLabel = new Label
+ {
+ AutomationId = "CurrentColorLabel",
+ FontSize = 14,
+ HorizontalOptions = LayoutOptions.Center,
+ Text = $"Current Primary: {Issue34931.InitialPrimaryColor}",
+ TextColor = Colors.Black
+ };
+
+ var changeColorButton = new Button
+ {
+ AutomationId = "ChangeColorButton",
+ HorizontalOptions = LayoutOptions.Fill,
+ Text = "Change Theme Color"
+ };
+
+ changeColorButton.Clicked += OnChangeColorClicked;
+
+ Content = new ScrollView
+ {
+ Content = new VerticalStackLayout
+ {
+ Padding = new Thickness(30, 0),
+ Spacing = 25,
+ VerticalOptions = LayoutOptions.Center,
+ Children =
+ {
+ new Label
+ {
+ Text = "Flyout DynamicResource Issue",
+ FontAttributes = FontAttributes.Bold,
+ FontSize = 24,
+ HorizontalOptions = LayoutOptions.Center,
+ },
+ new Label
+ {
+ Text = "1. Tap Change Theme Color to update the Primary DynamicResource at runtime.\n2. Open the flyout menu and switch between pages repeatedly.\n3. Reopen the flyout and verify the selected item reflects the updated color.",
+ HorizontalOptions = LayoutOptions.Center,
+ HorizontalTextAlignment = TextAlignment.Center,
+ },
+ changeColorButton,
+ _currentColorLabel,
+ }
+ }
+ };
+ }
+
+ void OnChangeColorClicked(object sender, EventArgs e)
+ {
+ _colorIndex = (_colorIndex + 1) % _colors.Length;
+ var colorValue = _colors[_colorIndex];
+
+ if (Application.Current is not null)
+ Application.Current.Resources["Primary"] = Color.FromArgb(colorValue);
+
+ _currentColorLabel.Text = $"Current Primary: {colorValue}";
+ }
+}
+
+public class Issue34931SecondPage : ContentPage
+{
+ public Issue34931SecondPage()
+ {
+ Title = "Second";
+ Content = new VerticalStackLayout
+ {
+ Padding = 30,
+ Spacing = 20,
+ VerticalOptions = LayoutOptions.Center,
+ Children =
+ {
+ new Label
+ {
+ Text = "Second Page",
+ FontSize = 32,
+ HorizontalOptions = LayoutOptions.Center,
+ },
+ new Label
+ {
+ AutomationId = "Issue34931SecondPageLabel",
+ Text = "Open the flyout and switch between pages repeatedly to reproduce the issue.",
+ HorizontalOptions = LayoutOptions.Center,
+ HorizontalTextAlignment = TextAlignment.Center,
+ }
+ }
+ };
+ }
+}
+
+public class Issue34931ThirdPage : ContentPage
+{
+ public Issue34931ThirdPage()
+ {
+ Title = "Third";
+ Content = new VerticalStackLayout
+ {
+ Padding = 30,
+ Spacing = 20,
+ VerticalOptions = LayoutOptions.Center,
+ Children =
+ {
+ new Label
+ {
+ Text = "Third Page",
+ FontSize = 32,
+ HorizontalOptions = LayoutOptions.Center,
+ },
+ new Label
+ {
+ AutomationId = "Issue34931ThirdPageLabel",
+ Text = "The selected flyout item should keep the updated DynamicResource styling.",
+ HorizontalOptions = LayoutOptions.Center,
+ HorizontalTextAlignment = TextAlignment.Center,
+ }
+ }
+ };
+ }
+}
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue35216.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue35216.cs
new file mode 100644
index 000000000000..2efc2cea608b
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue35216.cs
@@ -0,0 +1,100 @@
+using System.ComponentModel;
+using System.Runtime.CompilerServices;
+
+namespace Maui.Controls.Sample.Issues;
+
+[Issue(IssueTracker.Github, 35216, "SwipeItem IsVisible should properly refresh native swipe items when binding value changes dynamically", PlatformAffected.UWP)]
+public class Issue35216 : ContentPage
+{
+ readonly Issue35216ViewModel _viewModel = new() { IsDeleteVisible = false };
+ SwipeView _swipeView;
+
+ public Issue35216()
+ {
+ BindingContext = _viewModel;
+
+ SwipeItem deleteSwipeItem = new SwipeItem
+ {
+ Text = "Delete",
+ BackgroundColor = Colors.Green,
+ AutomationId = "DeleteSwipeItem"
+ };
+ deleteSwipeItem.SetBinding(SwipeItem.IsVisibleProperty, new Binding(nameof(Issue35216ViewModel.IsDeleteVisible)));
+
+ SwipeItem archiveSwipeItem = new SwipeItem
+ {
+ Text = "Archive",
+ BackgroundColor = Colors.Blue,
+ AutomationId = "ArchiveSwipeItem"
+ };
+
+ _swipeView = new SwipeView
+ {
+ HeightRequest = 60,
+ LeftItems = new SwipeItems { deleteSwipeItem, archiveSwipeItem },
+ Content = new Grid
+ {
+ BackgroundColor = Colors.LightGray,
+ Children =
+ {
+ new Label
+ {
+ AutomationId = "SwipeContent",
+ Text = "Swipe right to reveal items",
+ HorizontalOptions = LayoutOptions.Center,
+ VerticalOptions = LayoutOptions.Center
+ }
+ }
+ }
+ };
+
+ Button toggleButton = new Button
+ {
+ Text = "Toggle Delete Visibility",
+ AutomationId = "ToggleVisibilityButton"
+ };
+ toggleButton.Clicked += (s, e) => _viewModel.IsDeleteVisible = !_viewModel.IsDeleteVisible;
+
+ Button resetButton = new Button
+ {
+ Text = "Reset",
+ AutomationId = "ResetButton"
+ };
+ resetButton.Clicked += (s, e) => _viewModel.IsDeleteVisible = false;
+
+ Content = new VerticalStackLayout
+ {
+ Padding = new Thickness(20),
+ Spacing = 20,
+ Children =
+ {
+ _swipeView,
+ toggleButton,
+ resetButton,
+ }
+ };
+ }
+}
+
+public class Issue35216ViewModel : INotifyPropertyChanged
+{
+ bool _isDeleteVisible;
+
+ public bool IsDeleteVisible
+ {
+ get => _isDeleteVisible;
+ set
+ {
+ if (_isDeleteVisible != value)
+ {
+ _isDeleteVisible = value;
+ OnPropertyChanged();
+ }
+ }
+ }
+
+ public event PropertyChangedEventHandler PropertyChanged;
+
+ protected void OnPropertyChanged([CallerMemberName] string name = null) =>
+ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
+}
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue35386.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue35386.cs
new file mode 100644
index 000000000000..e068a0aa46ff
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue35386.cs
@@ -0,0 +1,242 @@
+#nullable enable
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace Maui.Controls.Sample.Issues;
+
+[Issue(IssueTracker.Github, 35386, "MauiView leaks detached platform views when SafeAreaEdges includes SoftInput", PlatformAffected.iOS)]
+public class Issue35386 : ContentPage
+{
+ const int CycleCount = 12;
+
+ readonly Grid _host;
+ readonly Label _status;
+ readonly VerticalStackLayout _log;
+ bool _started;
+
+ public Issue35386()
+ {
+ Title = "SoftInput observer leak repro";
+ BackgroundColor = Colors.White;
+
+ _status = new Label
+ {
+ Text = "Waiting to start...",
+ TextColor = Colors.Black,
+ FontSize = 16,
+ AutomationId = "statusLabel",
+ LineBreakMode = LineBreakMode.WordWrap
+ };
+
+ _log = new VerticalStackLayout
+ {
+ Spacing = 4
+ };
+
+ _host = new Grid
+ {
+ BackgroundColor = Color.FromArgb("#f3f6fb"),
+ HeightRequest = 160
+ };
+
+ var scrollView = new ScrollView();
+ var verticalStackLayout = new VerticalStackLayout();
+
+ var label = new Label
+ {
+ Text = "MAUI SoftInput Observer Leak Repro",
+ TextColor = Colors.Black,
+ FontSize = 22,
+ FontAttributes = FontAttributes.Bold
+ };
+ verticalStackLayout.Children.Add(label);
+ verticalStackLayout.Children.Add(_status);
+ verticalStackLayout.Children.Add(_host);
+ verticalStackLayout.Children.Add(_log);
+
+ scrollView.Content = verticalStackLayout;
+ Content = scrollView;
+
+ }
+
+ protected override async void OnAppearing()
+ {
+ base.OnAppearing();
+
+ if (_started)
+ return;
+
+ _started = true;
+ await Task.Delay(500);
+
+ try
+ {
+ await RunAsync();
+ }
+ catch (Exception ex)
+ {
+ Log("ERROR: " + ex);
+ _status.Text = "Repro failed: " + ex.Message;
+ //await ExitAsync(3);
+ }
+ }
+
+ async Task RunAsync()
+ {
+ Log("Running control scenario: SafeAreaEdges.None");
+ var none = await RunScenarioAsync("none", SafeAreaEdges.None);
+
+ Log("Running suspect scenario: SafeAreaEdges.SoftInput");
+ var softInput = await RunScenarioAsync("softinput", new SafeAreaEdges(SafeAreaRegions.SoftInput));
+
+ var proof = softInput.PlatformAlive > 0 && none.PlatformAlive == 0;
+ var summary =
+ $"RESULT: {(proof ? "LEAK REPRODUCED" : "NOT PROVEN")}\n" +
+ $"Control SafeAreaEdges.None: virtual={none.VirtualAlive}/{CycleCount}, handler={none.HandlerAlive}/{CycleCount}, platform={none.PlatformAlive}/{CycleCount}\n" +
+ $"Suspect SafeAreaEdges.SoftInput: virtual={softInput.VirtualAlive}/{CycleCount}, handler={softInput.HandlerAlive}/{CycleCount}, platform={softInput.PlatformAlive}/{CycleCount}\n";
+
+ _status.Text = summary;
+ Log(summary);
+
+ //await ExitAsync(proof ? 0 : 2);
+ }
+
+ async Task RunScenarioAsync(string name, SafeAreaEdges safeAreaEdges)
+ {
+ var probes = new List();
+
+ for (var i = 0; i < CycleCount; i++)
+ {
+ probes.Add(await CreateAndRemoveProbeAsync(name, safeAreaEdges, i));
+ await ForceGcAsync();
+ Log($"{name} cycle {i + 1}: platform alive={probes.Count(p => p.PlatformView.IsAlive)}");
+ }
+
+ await Task.Delay(500);
+ await ForceGcAsync();
+ await ForceGcAsync();
+
+ return new ScenarioResult(
+ name,
+ probes.Count(p => p.VirtualView.IsAlive),
+ probes.Count(p => p.Handler.IsAlive),
+ probes.Count(p => p.PlatformView.IsAlive));
+ }
+
+ async Task CreateAndRemoveProbeAsync(string scenarioName, SafeAreaEdges safeAreaEdges, int index)
+ {
+ WeakReference? virtualView = null;
+ WeakReference? handler = null;
+ WeakReference? platformView = null;
+
+ await MainThread.InvokeOnMainThreadAsync(async () =>
+ {
+ var probe = new Grid
+ {
+ SafeAreaEdges = safeAreaEdges,
+ AutomationId = $"{scenarioName}-probe-{index}",
+ HeightRequest = 96,
+ BackgroundColor = scenarioName == "softinput" ? Color.FromArgb("#ffe8e8") : Color.FromArgb("#e8f1ff"),
+ RowDefinitions =
+ {
+ new RowDefinition(GridLength.Auto),
+ new RowDefinition(GridLength.Star)
+ }
+ };
+
+ probe.Add(new Label
+ {
+ Text = $"{scenarioName} #{index}",
+ TextColor = Colors.Black,
+ Margin = new Thickness(8, 6, 8, 0)
+ });
+
+ probe.Add(new Entry
+ {
+ Text = "entry",
+ Margin = new Thickness(8),
+ AutomationId = $"{scenarioName}-entry-{index}"
+ }, row: 1);
+
+ _host.Children.Add(probe);
+ await WaitUntilLoadedAsync(probe);
+ await Task.Delay(100);
+
+ var currentHandler = probe.Handler;
+ var currentPlatformView = currentHandler?.PlatformView;
+
+ if (currentHandler is null || currentPlatformView is null)
+ throw new InvalidOperationException($"Probe {scenarioName} #{index} did not create a handler/platform view.");
+
+ virtualView = new WeakReference(probe);
+ handler = new WeakReference(currentHandler);
+ platformView = new WeakReference(currentPlatformView);
+
+ _host.Children.Remove(probe);
+ probe.DisconnectHandlers();
+
+ probe = null!;
+ currentHandler = null;
+ currentPlatformView = null;
+ });
+
+ await Task.Delay(250);
+ await ForceGcAsync();
+
+ return new ProbeRefs(
+ virtualView ?? throw new InvalidOperationException("Missing virtual view reference."),
+ handler ?? throw new InvalidOperationException("Missing handler reference."),
+ platformView ?? throw new InvalidOperationException("Missing platform view reference."));
+ }
+
+ static async Task WaitUntilLoadedAsync(VisualElement element)
+ {
+ if (element.IsLoaded)
+ return;
+
+ var loaded = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
+
+ void OnLoaded(object? sender, EventArgs args)
+ {
+ element.Loaded -= OnLoaded;
+ loaded.TrySetResult();
+ }
+
+ element.Loaded += OnLoaded;
+
+ var completed = await Task.WhenAny(loaded.Task, Task.Delay(TimeSpan.FromSeconds(3)));
+ element.Loaded -= OnLoaded;
+
+ if (completed != loaded.Task)
+ throw new TimeoutException("Probe view did not load.");
+ }
+
+ static async Task ForceGcAsync()
+ {
+ for (var i = 0; i < 4; i++)
+ {
+ GC.Collect();
+ GC.WaitForPendingFinalizers();
+ GC.Collect(2, GCCollectionMode.Forced, blocking: true);
+ await Task.Delay(50);
+ }
+ }
+
+ void Log(string message)
+ {
+ _log.Children.Add(new Label
+ {
+ Text = message,
+ TextColor = Colors.Black,
+ FontSize = 12,
+ LineBreakMode = LineBreakMode.WordWrap
+ });
+ }
+
+ readonly record struct ProbeRefs(WeakReference VirtualView, WeakReference Handler, WeakReference PlatformView);
+ readonly record struct ScenarioResult(string Name, int VirtualAlive, int HandlerAlive, int PlatformAlive);
+}
+
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue35471.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue35471.cs
new file mode 100644
index 000000000000..75d49ad0b44a
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue35471.cs
@@ -0,0 +1,67 @@
+namespace Maui.Controls.Sample.Issues;
+
+[Issue(IssueTracker.Github, 35471, "iOS Shell back button history menu does not update after runtime culture change", PlatformAffected.iOS | PlatformAffected.macOS)]
+public class Issue35471 : TestShell
+{
+ ContentPage _rootPage;
+
+ protected override void Init()
+ {
+ _rootPage = CreateContentPage("HomePage");
+ _rootPage.Title = "Home";
+
+ var navigateButton = new Button
+ {
+ AutomationId = "NavigateToDetail",
+ Text = "Go to Detail Page",
+ Command = new Command(async () =>
+ {
+ await Navigation.PushAsync(new Issue35471DetailPage(_rootPage));
+ })
+ };
+
+ _rootPage.Content = new VerticalStackLayout
+ {
+ Padding = 20,
+ Spacing = 10,
+ Children =
+ {
+ new Label { Text = "Root Page", AutomationId = "RootPageLabel" },
+ navigateButton
+ }
+ };
+ }
+
+ class Issue35471DetailPage : ContentPage
+ {
+ readonly ContentPage _previousPage;
+
+ public Issue35471DetailPage(ContentPage previousPage)
+ {
+ _previousPage = previousPage;
+ Title = "Detail";
+
+ var changeTitleButton = new Button
+ {
+ AutomationId = "ChangePreviousPageTitle",
+ Text = "Change Previous Page Title (simulate culture change)",
+ Command = new Command(() =>
+ {
+ // Simulate runtime culture change by updating the previous page's title
+ _previousPage.Title = "Accueil";
+ })
+ };
+
+ Content = new VerticalStackLayout
+ {
+ Padding = 20,
+ Spacing = 10,
+ Children =
+ {
+ new Label { Text = "Detail Page", FontSize = 20 },
+ changeTitleButton
+ }
+ };
+ }
+ }
+}
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue35490.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue35490.cs
new file mode 100644
index 000000000000..ed871615ee72
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue35490.cs
@@ -0,0 +1,41 @@
+using TabbedPage = Microsoft.Maui.Controls.TabbedPage;
+
+namespace Maui.Controls.Sample.Issues;
+
+[Issue(IssueTracker.Github, 35490, "[iOS 26] TabbedPage with NavigationPage children clips content above floating glass tab bar", PlatformAffected.iOS)]
+public class Issue35490 : TabbedPage
+{
+ public Issue35490()
+ {
+ var page1 = new ContentPage
+ {
+ Title = "Tab1",
+ BackgroundColor = Colors.MediumPurple,
+ Content = new Label
+ {
+ AutomationId = "Tab1Label",
+ Text = "Tab 1 — on iOS 26+, test passes if purple background extends under the floating tab bar",
+ TextColor = Colors.White,
+ VerticalOptions = LayoutOptions.Center,
+ HorizontalOptions = LayoutOptions.Center,
+ },
+ };
+
+ var page2 = new ContentPage
+ {
+ Title = "Tab2",
+ BackgroundColor = Colors.DarkCyan,
+ Content = new Label
+ {
+ AutomationId = "Tab2Label",
+ Text = "Tab 2 — on iOS 26+, test passes if cyan background extends under the floating tab bar",
+ TextColor = Colors.White,
+ VerticalOptions = LayoutOptions.Center,
+ HorizontalOptions = LayoutOptions.Center,
+ },
+ };
+
+ Children.Add(new NavigationPage(page1) { Title = "Tab1" });
+ Children.Add(new NavigationPage(page2) { Title = "Tab2" });
+ }
+}
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue35613.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue35613.cs
new file mode 100644
index 000000000000..198b56c19848
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue35613.cs
@@ -0,0 +1,344 @@
+using System.Diagnostics;
+using System.Text;
+
+namespace Maui.Controls.Sample.Issues;
+
+[Issue(IssueTracker.Github, 35613, "OnNavigatingFrom with a NavigationPage always has an incorrect DestinationPage parameter", PlatformAffected.All)]
+public class Issue35613 : NavigationPage
+{
+ static readonly List s_logEntries = new();
+ static event Action LogChanged;
+
+ public Issue35613()
+ {
+ Navigation.PushAsync(new Issue35613FirstPage());
+ }
+
+ public static void AppendLog(string message)
+ {
+ s_logEntries.Add(message);
+ Debug.WriteLine($"ISSUE35613: {message}");
+ Console.WriteLine($"ISSUE35613: {message}");
+ RaiseLogChanged();
+ }
+
+ public static void ClearLog()
+ {
+ s_logEntries.Clear();
+ RaiseLogChanged();
+ }
+
+ public static void SubscribeToLogChanged(Action callback)
+ {
+ LogChanged += callback;
+ }
+
+ public static void UnsubscribeFromLogChanged(Action callback)
+ {
+ LogChanged -= callback;
+ }
+
+ public static string GetLogText()
+ {
+ var builder = new StringBuilder();
+ foreach (var entry in s_logEntries)
+ {
+ builder.AppendLine(entry);
+ }
+ return builder.ToString();
+ }
+
+ static void RaiseLogChanged()
+ {
+ LogChanged?.Invoke();
+ }
+}
+
+public class Issue35613FirstPage : TestContentPage
+{
+ Editor _logEditor;
+
+ public Issue35613FirstPage()
+ {
+ Title = "Issue35613 First";
+ }
+
+ protected override void Init()
+ {
+ var navigateButton = new Button
+ {
+ Text = "Navigate to Second Page",
+ AutomationId = "Issue35613_NavigateButton"
+ };
+ navigateButton.Clicked += OnNavigateButtonClicked;
+
+ _logEditor = new Editor
+ {
+ AutomationId = "Issue35613_LogEditor",
+ IsReadOnly = true,
+ IsSpellCheckEnabled = false,
+ IsTextPredictionEnabled = false,
+ AutoSize = EditorAutoSizeOption.TextChanges,
+ MinimumHeightRequest = 220,
+ FontFamily = "Courier New",
+ FontSize = 12
+ };
+
+ Content = new VerticalStackLayout
+ {
+ Padding = 12,
+ Children =
+ {
+ navigateButton,
+ new Label { Text = "Event Log:", FontAttributes = FontAttributes.Bold },
+ _logEditor,
+ }
+ };
+ }
+
+ protected override void OnAppearing()
+ {
+ base.OnAppearing();
+ Issue35613.SubscribeToLogChanged(RefreshLogEditor);
+ RefreshLogEditor();
+ }
+
+ protected override void OnDisappearing()
+ {
+ Issue35613.UnsubscribeFromLogChanged(RefreshLogEditor);
+ base.OnDisappearing();
+ }
+
+ protected override void OnNavigatedTo(NavigatedToEventArgs args)
+ {
+ base.OnNavigatedTo(args);
+
+ var previousPage = args.PreviousPage?.GetType().Name ?? "Null";
+ Issue35613.AppendLog($"OnNavigatedTo FirstPage [{args.NavigationType}], PreviousPage={previousPage}");
+ }
+
+ protected override void OnNavigatingFrom(NavigatingFromEventArgs args)
+ {
+ base.OnNavigatingFrom(args);
+
+ var destinationPage = args.DestinationPage?.GetType().Name ?? "Null";
+ Issue35613.AppendLog($"OnNavigatingFrom FirstPage [{args.NavigationType}], DestinationPage={destinationPage}");
+ }
+
+ protected override void OnNavigatedFrom(NavigatedFromEventArgs args)
+ {
+ base.OnNavigatedFrom(args);
+
+ var destinationPage = args.DestinationPage?.GetType().Name ?? "Null";
+ Issue35613.AppendLog($"OnNavigatedFrom FirstPage [{args.NavigationType}], DestinationPage={destinationPage}");
+ }
+
+ void OnNavigateButtonClicked(object sender, EventArgs e)
+ {
+ Navigation.PushAsync(new Issue35613SecondPage());
+ }
+
+ void RefreshLogEditor()
+ {
+ if (_logEditor is null)
+ return;
+ _logEditor.Text = Issue35613.GetLogText();
+ }
+}
+
+public class Issue35613SecondPage : TestContentPage
+{
+ Editor _logEditor;
+
+ public Issue35613SecondPage()
+ {
+ Title = "Issue35613 Second";
+ }
+
+ protected override void Init()
+ {
+ var navigateBackButton = new Button
+ {
+ Text = "Navigate Back",
+ AutomationId = "Issue35613_NavigateBackButton"
+ };
+ navigateBackButton.Clicked += OnNavigateBackButtonClicked;
+
+ var navigateToThirdButton = new Button
+ {
+ Text = "Navigate to Third Page",
+ AutomationId = "Issue35613_NavigateToThirdButton"
+ };
+ navigateToThirdButton.Clicked += OnNavigateToThirdButtonClicked;
+
+ _logEditor = new Editor
+ {
+ AutomationId = "Issue35613_Second_LogEditor",
+ IsReadOnly = true,
+ IsSpellCheckEnabled = false,
+ IsTextPredictionEnabled = false,
+ AutoSize = EditorAutoSizeOption.TextChanges,
+ MinimumHeightRequest = 220,
+ FontFamily = "Courier New",
+ FontSize = 12
+ };
+
+ Content = new VerticalStackLayout
+ {
+ Padding = 12,
+ Children =
+ {
+ navigateBackButton,
+ navigateToThirdButton,
+ new Label { Text = "Event Log:", FontAttributes = FontAttributes.Bold },
+ _logEditor,
+ }
+ };
+ }
+
+ protected override void OnAppearing()
+ {
+ base.OnAppearing();
+ Issue35613.SubscribeToLogChanged(RefreshLogEditor);
+ RefreshLogEditor();
+ }
+
+ protected override void OnDisappearing()
+ {
+ Issue35613.UnsubscribeFromLogChanged(RefreshLogEditor);
+ base.OnDisappearing();
+ }
+
+ protected override void OnNavigatedTo(NavigatedToEventArgs args)
+ {
+ base.OnNavigatedTo(args);
+
+ var previousPage = args.PreviousPage?.GetType().Name ?? "Null";
+ Issue35613.AppendLog($"OnNavigatedTo SecondPage [{args.NavigationType}], PreviousPage={previousPage}");
+ }
+
+ protected override void OnNavigatingFrom(NavigatingFromEventArgs args)
+ {
+ base.OnNavigatingFrom(args);
+
+ var destinationPage = args.DestinationPage?.GetType().Name ?? "Null";
+ Issue35613.AppendLog($"OnNavigatingFrom SecondPage [{args.NavigationType}], DestinationPage={destinationPage}");
+ }
+
+ protected override void OnNavigatedFrom(NavigatedFromEventArgs args)
+ {
+ base.OnNavigatedFrom(args);
+
+ var destinationPage = args.DestinationPage?.GetType().Name ?? "Null";
+ Issue35613.AppendLog($"OnNavigatedFrom SecondPage [{args.NavigationType}], DestinationPage={destinationPage}");
+ }
+
+ void OnNavigateBackButtonClicked(object sender, EventArgs e)
+ {
+ Navigation.PopAsync();
+ }
+
+ void OnNavigateToThirdButtonClicked(object sender, EventArgs e)
+ {
+ Navigation.PushAsync(new Issue35613ThirdPage());
+ }
+
+ void RefreshLogEditor()
+ {
+ if (_logEditor is null)
+ return;
+ _logEditor.Text = Issue35613.GetLogText();
+ }
+}
+
+public class Issue35613ThirdPage : TestContentPage
+{
+ Editor _logEditor;
+
+ public Issue35613ThirdPage()
+ {
+ Title = "Issue35613 Third";
+ }
+
+ protected override void Init()
+ {
+ var popToRootButton = new Button
+ {
+ Text = "Pop To Root",
+ AutomationId = "Issue35613_PopToRootButton"
+ };
+ popToRootButton.Clicked += OnPopToRootButtonClicked;
+
+ _logEditor = new Editor
+ {
+ AutomationId = "Issue35613_Third_LogEditor",
+ IsReadOnly = true,
+ IsSpellCheckEnabled = false,
+ IsTextPredictionEnabled = false,
+ AutoSize = EditorAutoSizeOption.TextChanges,
+ MinimumHeightRequest = 220,
+ FontFamily = "Courier New",
+ FontSize = 12
+ };
+
+ Content = new VerticalStackLayout
+ {
+ Padding = 12,
+ Children =
+ {
+ popToRootButton,
+ new Label { Text = "Event Log:", FontAttributes = FontAttributes.Bold },
+ _logEditor,
+ }
+ };
+ }
+
+ protected override void OnAppearing()
+ {
+ base.OnAppearing();
+ Issue35613.SubscribeToLogChanged(RefreshLogEditor);
+ RefreshLogEditor();
+ }
+
+ protected override void OnDisappearing()
+ {
+ Issue35613.UnsubscribeFromLogChanged(RefreshLogEditor);
+ base.OnDisappearing();
+ }
+
+ protected override void OnNavigatedTo(NavigatedToEventArgs args)
+ {
+ base.OnNavigatedTo(args);
+
+ var previousPage = args.PreviousPage?.GetType().Name ?? "Null";
+ Issue35613.AppendLog($"OnNavigatedTo ThirdPage [{args.NavigationType}], PreviousPage={previousPage}");
+ }
+
+ protected override void OnNavigatingFrom(NavigatingFromEventArgs args)
+ {
+ base.OnNavigatingFrom(args);
+
+ var destinationPage = args.DestinationPage?.GetType().Name ?? "Null";
+ Issue35613.AppendLog($"OnNavigatingFrom ThirdPage [{args.NavigationType}], DestinationPage={destinationPage}");
+ }
+
+ protected override void OnNavigatedFrom(NavigatedFromEventArgs args)
+ {
+ base.OnNavigatedFrom(args);
+
+ var destinationPage = args.DestinationPage?.GetType().Name ?? "Null";
+ Issue35613.AppendLog($"OnNavigatedFrom ThirdPage [{args.NavigationType}], DestinationPage={destinationPage}");
+ }
+
+ void OnPopToRootButtonClicked(object sender, EventArgs e)
+ {
+ Navigation.PopToRootAsync();
+ }
+
+ void RefreshLogEditor()
+ {
+ if (_logEditor is null)
+ return;
+ _logEditor.Text = Issue35613.GetLogText();
+ }
+}
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue35675.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue35675.cs
new file mode 100644
index 000000000000..c197bc277af4
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue35675.cs
@@ -0,0 +1,59 @@
+namespace Maui.Controls.Sample.Issues;
+
+[Issue(IssueTracker.Github, 35675, "[iOS] CarouselView freezes with infinite loop when IsScrollAnimated=False", PlatformAffected.iOS)]
+public class Issue35675 : ContentPage
+{
+ readonly string[] _initialItems = ["Item 0", "Item 1", "Item 2"];
+ readonly string[] _updatedItems = ["Item 0b", "Item 1b", "Item 2b"];
+
+ public Issue35675()
+ {
+ Label instructionLabel = new Label
+ {
+ AutomationId = "InstructionLabel",
+ Text = "The test passes if the CarouselView is not frozen after the button click and the current item is updated properly.",
+ HorizontalOptions = LayoutOptions.Center,
+ HorizontalTextAlignment = TextAlignment.Center,
+ FontSize = 18
+ };
+
+ CarouselView carouselView = new CarouselView
+ {
+ AutomationId = "CarouselView",
+ HeightRequest = 300,
+ IsScrollAnimated = false,
+ BackgroundColor = Colors.LightGray,
+ HorizontalScrollBarVisibility = ScrollBarVisibility.Never,
+ ItemsSource = _initialItems,
+ ItemTemplate = new DataTemplate(() =>
+ {
+ Label label = new Label
+ {
+ FontSize = 24
+ };
+ label.SetBinding(Label.TextProperty, ".");
+ label.SetBinding(Label.AutomationIdProperty, ".");
+ return label;
+ })
+ };
+
+ Button scrollButton = new Button
+ {
+ Text = "Change Items And Scroll",
+ AutomationId = "ScrollButton"
+ };
+
+ scrollButton.Clicked += (s, e) =>
+ {
+ carouselView.ItemsSource = _updatedItems;
+ carouselView.CurrentItem = "Item 2b";
+ };
+
+ Content = new VerticalStackLayout
+ {
+ Padding = 20,
+ Spacing = 15,
+ Children = { instructionLabel, carouselView, scrollButton }
+ };
+ }
+}
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue35700.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue35700.cs
new file mode 100644
index 000000000000..f6d49cbef26d
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue35700.cs
@@ -0,0 +1,91 @@
+using System.Collections.ObjectModel;
+using Microsoft.Maui.Controls.Shapes;
+
+namespace Maui.Controls.Sample.Issues;
+
+[Issue(IssueTracker.Github, 35700,
+ "Grouped CollectionView items not rendered properly on Android with GridItemsLayout",
+ PlatformAffected.Android)]
+public class Issue35700 : TestContentPage
+{
+ protected override void Init()
+ {
+ var collectionView = new CollectionView2
+ {
+ AutomationId = "TestCollectionView",
+ IsGrouped = true,
+ HorizontalOptions = LayoutOptions.Fill,
+ Margin = new Thickness(5, 30, 5, 5),
+ ItemsLayout = new GridItemsLayout(ItemsLayoutOrientation.Vertical)
+ {
+ Span = 5,
+ VerticalItemSpacing = 30,
+ HorizontalItemSpacing = 10,
+ },
+ GroupHeaderTemplate = new DataTemplate(() =>
+ {
+ var label = new Label
+ {
+ HorizontalOptions = LayoutOptions.Fill,
+ HorizontalTextAlignment = TextAlignment.Start,
+ Padding = new Thickness(10),
+ FontSize = 18,
+ TextColor = Colors.White,
+ FontAttributes = FontAttributes.Bold,
+ BackgroundColor = Colors.Gray,
+ };
+ label.SetBinding(Label.TextProperty, "Name");
+ return label;
+ }),
+ ItemTemplate = new DataTemplate(() =>
+ {
+ var label = new Label
+ {
+ HorizontalOptions = LayoutOptions.Center,
+ TextColor = Colors.White,
+ VerticalOptions = LayoutOptions.Center,
+ HorizontalTextAlignment = TextAlignment.Center,
+ };
+ label.SetBinding(Label.TextProperty, ".");
+
+ return new Border
+ {
+ StrokeShape = new RoundRectangle { CornerRadius = 10 },
+ Padding = new Thickness(5),
+ MinimumWidthRequest = 50,
+ Stroke = Colors.Transparent,
+ BackgroundColor = Colors.Gray,
+ StrokeThickness = 1,
+ HorizontalOptions = LayoutOptions.Center,
+ Content = label,
+ };
+ }),
+ };
+
+ collectionView.ItemsSource = new ObservableCollection
+ {
+ new NumberGroup35700("100s", new List
+ {
+ "100", "200", "300", "400", "500",
+ "600", "700", "800", "900",
+ }),
+ new NumberGroup35700("1000s", new List
+ {
+ "1000", "2000", "3000", "4000", "5000",
+ "6000", "7000", "8000", "9000",
+ }),
+ };
+
+ Content = collectionView;
+ }
+}
+
+public class NumberGroup35700 : ObservableCollection
+{
+ public string Name { get; private set; }
+
+ public NumberGroup35700(string name, List numbers) : base(numbers)
+ {
+ Name = name;
+ }
+}
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue35736.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue35736.cs
new file mode 100644
index 000000000000..0972a415bb8f
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue35736.cs
@@ -0,0 +1,146 @@
+namespace Maui.Controls.Sample.Issues;
+
+[Issue(IssueTracker.Github, 35736, "SearchHandler QueryIcon, ClearIcon, ClearPlaceholderIcon need to update visually at runtime", PlatformAffected.iOS | PlatformAffected.Android | PlatformAffected.UWP)]
+public class Issue35736 : Shell
+{
+ public Issue35736()
+ {
+ var page = new Issue35736Page();
+ Items.Add(new ShellContent
+ {
+ Title = "Search Icon Test",
+ Content = page
+ });
+ }
+
+ public class Issue35736Page : ContentPage
+ {
+ readonly SearchHandler _searchHandler;
+ bool _useAltQueryIcon;
+ bool _useAltClearIcon;
+ bool _useAltClearPlaceholderIcon;
+ bool _clearPlaceholderEnabled = true;
+
+ readonly Label _queryIconLabel;
+ readonly Label _clearIconLabel;
+ readonly Label _clearPlaceholderIconLabel;
+ readonly Label _clearPlaceholderEnabledLabel;
+ readonly Button _toggleClearPlaceholderEnabledBtn;
+
+ public Issue35736Page()
+ {
+ Title = "Search Icon Test";
+
+ _searchHandler = new SearchHandler
+ {
+ Placeholder = "Search items...",
+ AutomationId = "Issue35736SearchHandler",
+ QueryIcon = ImageSource.FromFile("bank.png"),
+ ClearIcon = ImageSource.FromFile("bank.png"),
+ ClearPlaceholderIcon = ImageSource.FromFile("bank.png"),
+ ClearPlaceholderEnabled = true,
+ ShowsResults = false,
+ };
+
+ Shell.SetSearchHandler(this, _searchHandler);
+
+ _queryIconLabel = new Label { AutomationId = "Issue35736QueryIconLabel", Text = "QueryIcon: bank.png" };
+ _clearIconLabel = new Label { AutomationId = "Issue35736ClearIconLabel", Text = "ClearIcon: bank.png" };
+ _clearPlaceholderIconLabel = new Label { AutomationId = "Issue35736ClearPlaceholderIconLabel", Text = "ClearPlaceholderIcon: bank.png" };
+ _clearPlaceholderEnabledLabel = new Label { AutomationId = "Issue35736ClearPlaceholderEnabledLabel", Text = "ClearPlaceholderEnabled: True" };
+
+ var toggleQueryIconBtn = new Button { Text = "Toggle QueryIcon", AutomationId = "Issue35736ToggleQueryIcon" };
+ toggleQueryIconBtn.Clicked += OnToggleQueryIcon;
+
+ var toggleClearIconBtn = new Button { Text = "Toggle ClearIcon", AutomationId = "Issue35736ToggleClearIcon" };
+ toggleClearIconBtn.Clicked += OnToggleClearIcon;
+
+ var toggleClearPlaceholderIconBtn = new Button { Text = "Toggle ClearPlaceholderIcon", AutomationId = "Issue35736ToggleClearPlaceholderIcon" };
+ toggleClearPlaceholderIconBtn.Clicked += OnToggleClearPlaceholderIcon;
+
+ _toggleClearPlaceholderEnabledBtn = new Button
+ {
+ Text = "Toggle ClearPlaceholderEnabled (Current: True)",
+ AutomationId = "Issue35736ToggleClearPlaceholderEnabled"
+ };
+ _toggleClearPlaceholderEnabledBtn.Clicked += OnToggleClearPlaceholderEnabled;
+
+ var resetBtn = new Button { Text = "Reset All to Defaults", AutomationId = "Issue35736ResetAll" };
+ resetBtn.Clicked += OnResetAll;
+
+ Content = new ScrollView
+ {
+ Content = new VerticalStackLayout
+ {
+ Padding = new Thickness(30, 0),
+ Spacing = 15,
+ Children =
+ {
+ new Label { Text = "SearchHandler Icon Demo", FontSize = 20, FontAttributes = FontAttributes.Bold, HorizontalOptions = LayoutOptions.Center },
+ new VerticalStackLayout
+ {
+ Spacing = 4,
+ Children = { _queryIconLabel, _clearIconLabel, _clearPlaceholderIconLabel, _clearPlaceholderEnabledLabel }
+ },
+ toggleQueryIconBtn,
+ toggleClearIconBtn,
+ toggleClearPlaceholderIconBtn,
+ _toggleClearPlaceholderEnabledBtn,
+ resetBtn,
+ }
+ }
+ };
+ }
+
+ void OnToggleQueryIcon(object sender, EventArgs e)
+ {
+ _useAltQueryIcon = !_useAltQueryIcon;
+ var icon = _useAltQueryIcon ? "calculator.png" : "bank.png";
+ _searchHandler.QueryIcon = ImageSource.FromFile(icon);
+ _queryIconLabel.Text = $"QueryIcon: {icon}";
+ }
+
+ void OnToggleClearIcon(object sender, EventArgs e)
+ {
+ _useAltClearIcon = !_useAltClearIcon;
+ var icon = _useAltClearIcon ? "calculator.png" : "bank.png";
+ _searchHandler.ClearIcon = ImageSource.FromFile(icon);
+ _clearIconLabel.Text = $"ClearIcon: {icon}";
+ }
+
+ void OnToggleClearPlaceholderIcon(object sender, EventArgs e)
+ {
+ _useAltClearPlaceholderIcon = !_useAltClearPlaceholderIcon;
+ var icon = _useAltClearPlaceholderIcon ? "calculator.png" : "bank.png";
+ _searchHandler.ClearPlaceholderIcon = ImageSource.FromFile(icon);
+ _clearPlaceholderIconLabel.Text = $"ClearPlaceholderIcon: {icon}";
+ }
+
+ void OnToggleClearPlaceholderEnabled(object sender, EventArgs e)
+ {
+ _clearPlaceholderEnabled = !_clearPlaceholderEnabled;
+ _searchHandler.ClearPlaceholderEnabled = _clearPlaceholderEnabled;
+ _clearPlaceholderEnabledLabel.Text = $"ClearPlaceholderEnabled: {_clearPlaceholderEnabled}";
+ _toggleClearPlaceholderEnabledBtn.Text = $"Toggle ClearPlaceholderEnabled (Current: {_clearPlaceholderEnabled})";
+ }
+
+ void OnResetAll(object sender, EventArgs e)
+ {
+ _useAltQueryIcon = false;
+ _useAltClearIcon = false;
+ _useAltClearPlaceholderIcon = false;
+ _clearPlaceholderEnabled = true;
+
+ _searchHandler.QueryIcon = null;
+ _searchHandler.ClearIcon = null;
+ _searchHandler.ClearPlaceholderIcon = null;
+ _searchHandler.ClearPlaceholderEnabled = true;
+
+ _queryIconLabel.Text = "QueryIcon: default";
+ _clearIconLabel.Text = "ClearIcon: default";
+ _clearPlaceholderIconLabel.Text = "ClearPlaceholderIcon: default";
+ _clearPlaceholderEnabledLabel.Text = "ClearPlaceholderEnabled: True";
+ _toggleClearPlaceholderEnabledBtn.Text = "Toggle ClearPlaceholderEnabled (Current: True)";
+ }
+ }
+}
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue35752.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue35752.cs
new file mode 100644
index 000000000000..9847213af94b
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue35752.cs
@@ -0,0 +1,80 @@
+namespace Maui.Controls.Sample.Issues;
+
+[Issue(IssueTracker.Github, 35752, "Android DragGestureRecognizer DragStarting fires prematurely on tap", PlatformAffected.Android)]
+public class Issue35752 : TestContentPage
+{
+ protected override void Init()
+ {
+ var statusLabel = new Label
+ {
+ Text = "Ready",
+ AutomationId = "StatusLabel"
+ };
+
+ var dragCountLabel = new Label
+ {
+ Text = "0",
+ AutomationId = "DragStartCount"
+ };
+
+ int dragStartCount = 0;
+
+ var dragRecognizer = new DragGestureRecognizer();
+ dragRecognizer.DragStarting += (s, e) =>
+ {
+ dragStartCount++;
+ dragCountLabel.Text = dragStartCount.ToString();
+ statusLabel.Text = "DragStarting fired";
+ };
+
+ var dragBox = new Label
+ {
+ HeightRequest = 100,
+ WidthRequest = 200,
+ BackgroundColor = Colors.Blue,
+ AutomationId = "DragBox",
+ Text = "Drag Me",
+ TextColor = Colors.White,
+ HorizontalTextAlignment = TextAlignment.Center,
+ VerticalTextAlignment = TextAlignment.Center,
+ GestureRecognizers = { dragRecognizer }
+ };
+
+ var dropRecognizer = new DropGestureRecognizer();
+ var dropBox = new Label
+ {
+ HeightRequest = 100,
+ WidthRequest = 200,
+ BackgroundColor = Colors.Green,
+ AutomationId = "DropBox",
+ Text = "Drop Here",
+ TextColor = Colors.White,
+ HorizontalTextAlignment = TextAlignment.Center,
+ VerticalTextAlignment = TextAlignment.Center,
+ GestureRecognizers = { dropRecognizer }
+ };
+
+ var instructions = new Label
+ {
+ Text = "Quick tap the blue box - DragStarting should NOT fire. " +
+ "Long press or drag it - DragStarting SHOULD fire.",
+ AutomationId = "TestLoaded"
+ };
+
+ Content = new VerticalStackLayout
+ {
+ Spacing = 20,
+ Padding = new Thickness(20),
+ Children =
+ {
+ instructions,
+ dragBox,
+ dropBox,
+ new Label { Text = "Status:" },
+ statusLabel,
+ new Label { Text = "DragStart count:" },
+ dragCountLabel
+ }
+ };
+ }
+}
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue35755.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue35755.cs
new file mode 100644
index 000000000000..21fe6fa1dc6d
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue35755.cs
@@ -0,0 +1,70 @@
+namespace Maui.Controls.Sample.Issues;
+
+[Issue(IssueTracker.Github, 35755, "IndexOutOfBoundsException in RecalculateSpanPositions when a Label uses FormattedText, MaxLines, and TailTruncation", PlatformAffected.Android)]
+public class Issue35755 : ContentPage
+{
+ readonly string _paragraphA = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum eleifend, augue nec aliquam interdum, massa nisl viverra orci, non interdum risus arcu id lorem. Curabitur accumsan, urna eu tempor tincidunt, purus neque feugiat tortor, sed tristique nibh nunc et augue.";
+ readonly string _paragraphB = "Aliquam erat volutpat. Quisque a mi lacus. Integer vitae malesuada sem. Nunc id dui nec lacus feugiat volutpat. Morbi et sollicitudin erat. Sed varius felis id dignissim facilisis. Vivamus vulputate, augue sed finibus laoreet, enim neque tristique odio, id rhoncus elit purus a turpis.";
+ readonly string _paragraphC = "Praesent in lectus non mauris mattis ultrices. Donec non justo ac nunc porta pellentesque. Integer euismod, velit in posuere iaculis, lorem nunc commodo libero, nec interdum lorem nibh ut turpis. Phasellus gravida tristique tortor, id posuere turpis sodales in.";
+
+ Label _crashTargetLabel;
+
+ public Issue35755()
+ {
+ _crashTargetLabel = new Label
+ {
+ AutomationId = "CrashTargetLabel",
+ MaxLines = 4,
+ LineBreakMode = LineBreakMode.TailTruncation,
+ FontSize = 14
+ };
+
+ var resultLabel = new Label
+ {
+ AutomationId = "ResultLabel",
+ Text = "Waiting for trigger..."
+ };
+
+ var triggerButton = new Button
+ {
+ AutomationId = "TriggerButton",
+ Text = "Trigger FormattedText"
+ };
+
+ triggerButton.Clicked += (s, e) =>
+ {
+ _crashTargetLabel.FormattedText = BuildFormattedText();
+ resultLabel.Text = "Success";
+ };
+
+ Content = new ScrollView
+ {
+ Content = new VerticalStackLayout
+ {
+ Padding = new Thickness(24),
+ Spacing = 16,
+ Children =
+ {
+ triggerButton,
+ resultLabel,
+ _crashTargetLabel
+ }
+ }
+ };
+ }
+
+ FormattedString BuildFormattedText()
+ {
+ var fs = new FormattedString();
+ fs.Spans.Add(new Span
+ {
+ Text = "Content: ",
+ FontAttributes = FontAttributes.Bold,
+ TextColor = Colors.DarkRed
+ });
+ fs.Spans.Add(new Span { Text = _paragraphA + "\n\n", TextColor = Colors.Black });
+ fs.Spans.Add(new Span { Text = _paragraphB + "\n\n", TextColor = Colors.DarkBlue });
+ fs.Spans.Add(new Span { Text = _paragraphC, TextColor = Colors.DarkGreen });
+ return fs;
+ }
+}
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue35764.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue35764.cs
new file mode 100644
index 000000000000..8a05502e33cc
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue35764.cs
@@ -0,0 +1,73 @@
+namespace Maui.Controls.Sample.Issues;
+
+[Issue(IssueTracker.Github, 35764, "[Android] SearchHandler.ClearPlaceholderEnabled has no effect", PlatformAffected.Android)]
+public partial class Issue35764 : TestShell
+{
+ protected override void Init()
+ {
+ FlyoutBehavior = FlyoutBehavior.Disabled;
+
+ Items.Add(new ShellContent
+ {
+ Title = "Home",
+ Route = "MainPage",
+ Content = new Issue35764ContentPage() { Title = "Home" }
+ });
+ }
+
+ class Issue35764ContentPage : ContentPage
+ {
+ public Issue35764ContentPage()
+ {
+ SafeAreaEdges = new SafeAreaEdges(SafeAreaRegions.Container);
+
+ SearchHandler searchHandler = new SearchHandler
+ {
+ AutomationId = "Issue35764SearchHandler",
+ Placeholder = "Search",
+ ClearPlaceholderEnabled = true,
+ ClearPlaceholderIcon = "bank.png",
+ };
+
+ Shell.SetSearchHandler(this, searchHandler);
+
+ Label descriptionLabel = new Label
+ {
+ Text = "The test passes if the ClearPlaceholder icon respects ClearPlaceholderEnabled visibility.",
+ HorizontalTextAlignment = TextAlignment.Center,
+ };
+
+ Label statusLabel = new Label
+ {
+ Text = $"ClearPlaceholderEnabled: {searchHandler.ClearPlaceholderEnabled}",
+ AutomationId = "ClearPlaceholderEnabledStatus",
+ HorizontalTextAlignment = TextAlignment.Center,
+ };
+
+ Button toggleButton = new Button
+ {
+ Text = "Disable ClearPlaceholder",
+ AutomationId = "ToggleClearPlaceholderEnabled",
+ HorizontalOptions = LayoutOptions.Center,
+ };
+
+ toggleButton.Clicked += (_, _) =>
+ {
+ searchHandler.ClearPlaceholderEnabled = !searchHandler.ClearPlaceholderEnabled;
+ toggleButton.Text = searchHandler.ClearPlaceholderEnabled
+ ? "Disable ClearPlaceholder"
+ : "Enable ClearPlaceholder";
+ statusLabel.Text = $"ClearPlaceholderEnabled: {searchHandler.ClearPlaceholderEnabled}";
+ };
+
+ Content = new VerticalStackLayout
+ {
+ Spacing = 12,
+ Padding = 20,
+ HorizontalOptions = LayoutOptions.Center,
+ VerticalOptions = LayoutOptions.Center,
+ Children = { descriptionLabel, statusLabel, toggleButton },
+ };
+ }
+ }
+}
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue35771.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue35771.cs
new file mode 100644
index 000000000000..b42010e6b6c4
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue35771.cs
@@ -0,0 +1,140 @@
+namespace Maui.Controls.Sample.Issues;
+
+[Issue(IssueTracker.Github, 35771, "Android SIGSEGV crash with multiple auto-sizing WebViews in ScrollView on navigated page", PlatformAffected.Android)]
+public class Issue35771 : NavigationPage
+{
+ public Issue35771() : base(new Issue35771HomePage()) { }
+
+ class Issue35771HomePage : ContentPage
+ {
+ public Issue35771HomePage()
+ {
+ Content = new VerticalStackLayout
+ {
+ VerticalOptions = LayoutOptions.Center,
+ Children =
+ {
+ new Button
+ {
+ AutomationId = "Issue35771NavigateButton",
+ Text = "Open repro page",
+ Command = new Command(async () => await Navigation.PushAsync(new Issue35771ReproPage()))
+ },
+ new Button
+ {
+ AutomationId = "Issue35771HorizontalNavigateButton",
+ Text = "Open horizontal repro page",
+ Command = new Command(async () => await Navigation.PushAsync(new Issue35771HorizontalReproPage()))
+ },
+ new Button
+ {
+ AutomationId = "Issue35771PopAsyncNavigateButton",
+ Text = "Open PopAsync repro page",
+ Command = new Command(async () => await Navigation.PushAsync(new Issue35771PopAsyncReproPage()))
+ }
+ }
+ };
+ }
+ }
+
+ class Issue35771ReproPage : ContentPage
+ {
+ public Issue35771ReproPage()
+ {
+ var stack = new VerticalStackLayout
+ {
+ Children =
+ {
+ new Label
+ {
+ AutomationId = "Issue35771Ready",
+ Text = "Page loaded — no crash"
+ }
+ }
+ };
+
+ for (int i = 1; i <= 6; i++)
+ {
+ stack.Children.Add(new WebView
+ {
+ Source = new HtmlWebViewSource { Html = $"WebView {i}
" }
+ });
+ }
+
+ Content = new ScrollView { Content = stack };
+ }
+ }
+
+ // Reproduces the horizontal auto-sizing crash scenario (width == 0, height > 0):
+ // WebViews inside a HorizontalStackLayout have a fixed HeightRequest but no WidthRequest,
+ // so the first layout pass produces (w=0, h>0) on the native view.
+ class Issue35771HorizontalReproPage : ContentPage
+ {
+ public Issue35771HorizontalReproPage()
+ {
+ var stack = new HorizontalStackLayout();
+
+ for (int i = 1; i <= 6; i++)
+ {
+ stack.Children.Add(new WebView
+ {
+ HeightRequest = 200,
+ Source = new HtmlWebViewSource { Html = $"WebView {i}
" }
+ });
+ }
+
+ Content = new ScrollView
+ {
+ Orientation = ScrollOrientation.Horizontal,
+ Content = new VerticalStackLayout
+ {
+ Children =
+ {
+ new Label
+ {
+ AutomationId = "Issue35771HorizontalReady",
+ Text = "Horizontal page loaded — no crash"
+ },
+ stack
+ }
+ }
+ };
+ }
+ }
+
+ // Reproduces the PopAsync crash scenario: popping a page while WebViews are still in
+ // the dangerous (w>0, h=0) first-layout state previously caused a RenderThread SIGSEGV.
+ class Issue35771PopAsyncReproPage : ContentPage
+ {
+ public Issue35771PopAsyncReproPage()
+ {
+ var stack = new VerticalStackLayout
+ {
+ Children =
+ {
+ new Label
+ {
+ AutomationId = "Issue35771PopAsyncReady",
+ Text = "Page loaded — tap button to pop"
+ },
+ new Button
+ {
+ AutomationId = "Issue35771PopAsyncPopButton",
+ Text = "Pop back",
+ Command = new Command(async () => await Navigation.PopAsync())
+ }
+ }
+ };
+
+ for (int i = 1; i <= 6; i++)
+ {
+ stack.Children.Add(new WebView
+ {
+ Source = new HtmlWebViewSource { Html = $"WebView {i}
" }
+ });
+ }
+
+ Content = new ScrollView { Content = stack };
+ }
+ }
+}
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue35788.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue35788.cs
new file mode 100644
index 000000000000..12c95524a5b8
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue35788.cs
@@ -0,0 +1,51 @@
+namespace Maui.Controls.Sample.Issues;
+
+[Issue(IssueTracker.Github, 35788, "[Android] WebView CanGoBack returns true unexpectedly on first page due to spurious about:blank history entry", PlatformAffected.Android)]
+public class Issue35788 : ContentPage
+{
+ const string StatusLabelId = "Issue35788StatusLabel";
+ const string NavigateButtonId = "Issue35788NavigateButton";
+
+ readonly Label _statusLabel;
+ readonly WebView _webView;
+
+ public Issue35788()
+ {
+ _statusLabel = new Label
+ {
+ AutomationId = StatusLabelId,
+ Text = "Waiting"
+ };
+
+ _webView = new WebView
+ {
+ HeightRequest = 300
+ };
+
+ _webView.Navigated += OnWebViewNavigated;
+
+ var navigateButton = new Button
+ {
+ AutomationId = NavigateButtonId,
+ Text = "Load Page"
+ };
+
+ navigateButton.Clicked += (s, e) =>
+ _webView.Source = new HtmlWebViewSource { Html = "Hello " };
+
+ Content = new VerticalStackLayout
+ {
+ Padding = 20,
+ Spacing = 10,
+ Children = { navigateButton, _statusLabel, _webView }
+ };
+ }
+
+ void OnWebViewNavigated(object sender, WebNavigatedEventArgs e)
+ {
+ if (e.Result == WebNavigationResult.Success)
+ _statusLabel.Text = _webView.CanGoBack ? "CanGoBack=True" : "CanGoBack=False";
+ else
+ _statusLabel.Text = $"NavFailed:{e.Result}";
+ }
+}
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue35806.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue35806.cs
new file mode 100644
index 000000000000..387a8079853e
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue35806.cs
@@ -0,0 +1,92 @@
+using System.Collections.ObjectModel;
+using Maui.Controls.Sample.Issues;
+
+namespace Controls.TestCases.HostApp.Issues;
+
+[Issue(IssueTracker.Github, 35806, "Android CollectionView KeepScrollOffset stops working after replacing ItemsSource", PlatformAffected.Android)]
+public class Issue35806 : TestContentPage
+{
+ ObservableCollection items;
+ CollectionView collectionView;
+ int sourceVersion = 1;
+
+ protected override void Init()
+ {
+ items = CreateItemsSource(sourceVersion);
+
+ collectionView = new CollectionView
+ {
+ AutomationId = "CollectionView35806",
+ ItemsSource = items,
+ ItemsUpdatingScrollMode = ItemsUpdatingScrollMode.KeepScrollOffset,
+ ItemTemplate = new DataTemplate(() =>
+ {
+ var label = new Label();
+ label.SetBinding(Label.TextProperty, ".");
+ return new Border
+ {
+ Content = label,
+ Padding = 10,
+ Margin = new Thickness(5),
+ BackgroundColor = Colors.LightGray,
+ };
+ })
+ };
+
+ var replaceSourceButton = new Button
+ {
+ Text = "Replace ItemsSource",
+ AutomationId = "ReplaceSourceButton",
+ Command = new Command(() =>
+ {
+ sourceVersion++;
+ items = CreateItemsSource(sourceVersion);
+ collectionView.ItemsSource = items;
+ })
+ };
+
+ var scrollToTopButton = new Button
+ {
+ Text = "Scroll To Top",
+ AutomationId = "ScrollToTopButton",
+ Command = new Command(() =>
+ {
+ collectionView.ScrollTo(0, position: ScrollToPosition.Start, animate: false);
+ })
+ };
+
+ var insertAtTopButton = new Button
+ {
+ Text = "Insert At Top",
+ AutomationId = "InsertAtTopButton",
+ Command = new Command(() =>
+ {
+ items.Insert(0, $"Inserted-{items.Count + 1}");
+ })
+ };
+
+ var grid = new Grid
+ {
+ RowDefinitions =
+ {
+ new RowDefinition { Height = GridLength.Auto },
+ new RowDefinition { Height = GridLength.Auto },
+ new RowDefinition { Height = GridLength.Auto },
+ new RowDefinition { Height = GridLength.Star }
+ },
+ RowSpacing = 5
+ };
+ grid.Add(replaceSourceButton, 0, 0);
+ grid.Add(scrollToTopButton, 0, 1);
+ grid.Add(insertAtTopButton, 0, 2);
+ grid.Add(collectionView, 0, 3);
+
+ Content = grid;
+ }
+
+ static ObservableCollection CreateItemsSource(int version)
+ {
+ return new ObservableCollection(
+ Enumerable.Range(1, 30).Select(i => $"v{version}-Item {i}"));
+ }
+}
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue35844.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue35844.cs
new file mode 100644
index 000000000000..cd2b31a82af0
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue35844.cs
@@ -0,0 +1,74 @@
+namespace Maui.Controls.Sample.Issues;
+
+[Issue(IssueTracker.Github, 35844, "Shell TitleView does not resize after rotation on iOS 26+", PlatformAffected.iOS)]
+public class Issue35844Shell : Shell
+{
+ public Issue35844Shell()
+ {
+ Issue35844 contentPage = new Issue35844();
+
+ ShellContent shellContent = new ShellContent
+ {
+ Content = contentPage,
+ Route = "Issue35844"
+ };
+
+ Items.Add(shellContent);
+ }
+}
+
+public class Issue35844 : ContentPage
+{
+ public Issue35844()
+ {
+ Shell.SetTitleView(this, new Grid
+ {
+ BackgroundColor = Colors.LightBlue,
+ AutomationId = "TitleViewGrid",
+ HorizontalOptions = LayoutOptions.Fill,
+ Children =
+ {
+ new Label
+ {
+ Text = "Shell TitleView",
+ AutomationId = "TitleLabel",
+ TextColor = Colors.White,
+ FontSize = 18,
+ FontAttributes = FontAttributes.Bold,
+ VerticalOptions = LayoutOptions.Center,
+ HorizontalOptions = LayoutOptions.Center
+ }
+ }
+ });
+
+ Content = new VerticalStackLayout
+ {
+ Padding = new Thickness(20),
+ Spacing = 10,
+ Children =
+ {
+ new Label
+ {
+ Text = "Issue 35844",
+ FontSize = 20,
+ FontAttributes = FontAttributes.Bold,
+ AutomationId = "HeaderLabel",
+ HorizontalOptions = LayoutOptions.Center
+ },
+ new Label
+ {
+ Text = "Shell TitleView should fill the navigation bar width after rotation on iOS 26+.",
+ FontSize = 14,
+ AutomationId = "DescriptionLabel"
+ },
+ new Label
+ {
+ Text = "Rotate device to test",
+ AutomationId = "StatusLabel",
+ FontSize = 16,
+ TextColor = Colors.Gray
+ }
+ }
+ };
+ }
+}
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue35859.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue35859.cs
new file mode 100644
index 000000000000..454668546915
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue35859.cs
@@ -0,0 +1,297 @@
+using System.Collections.Generic;
+
+namespace Maui.Controls.Sample.Issues;
+
+[Issue(IssueTracker.Github, 35859, "CollectionView2 on iOS measures non-first cells despite ItemSizingStrategy.MeasureFirstItem", PlatformAffected.iOS | PlatformAffected.macOS)]
+public class Issue35859 : ContentPage
+{
+ readonly Label _summaryLabel;
+ readonly CollectionView2 _collectionView;
+
+ public Issue35859()
+ {
+ Title = "Issue 35859";
+
+ MeasureFirstItemProbeRegistry.Reset();
+ MeasureFirstItemProbeRegistry.MeasurementsChanged += OnMeasurementsChanged;
+
+ _summaryLabel = new Label
+ {
+ AutomationId = "35859Summary",
+ Margin = new Thickness(12, 10),
+ FontSize = 13
+ };
+
+ var resetButton = new Button
+ {
+ Text = "Reset",
+ AutomationId = "35859ResetButton"
+ };
+ resetButton.Clicked += (_, _) => ResetProof();
+
+ var scrollButton = new Button
+ {
+ Text = "Scroll to 40",
+ AutomationId = "35859ScrollTo40Button"
+ };
+ scrollButton.Clicked += (_, _) => ScrollToItem40();
+
+ var buttons = new HorizontalStackLayout
+ {
+ Spacing = 8,
+ Margin = new Thickness(12, 0, 12, 10),
+ Children = { resetButton, scrollButton }
+ };
+
+ _collectionView = CreateCollectionView();
+
+ var headerLabel = new Label
+ {
+ Text = "MeasureFirstItem regression probe for CV2",
+ Margin = new Thickness(12, 10, 12, 0),
+ FontAttributes = FontAttributes.Bold
+ };
+
+ var layout = new Grid
+ {
+ RowDefinitions =
+ {
+ new RowDefinition { Height = GridLength.Auto },
+ new RowDefinition { Height = GridLength.Auto },
+ new RowDefinition { Height = GridLength.Auto },
+ new RowDefinition { Height = GridLength.Star }
+ }
+ };
+
+ layout.Children.Add(headerLabel);
+ Grid.SetRow(headerLabel, 0);
+
+ layout.Children.Add(_summaryLabel);
+ Grid.SetRow(_summaryLabel, 1);
+
+ layout.Children.Add(buttons);
+ Grid.SetRow(buttons, 2);
+
+ layout.Children.Add(_collectionView);
+ Grid.SetRow(_collectionView, 3);
+
+ Content = layout;
+
+ UpdateSummary();
+ }
+
+ static CollectionView2 CreateCollectionView()
+ {
+ return new CollectionView2
+ {
+ AutomationId = "35859Items2CV2CollectionView",
+ ItemSizingStrategy = ItemSizingStrategy.MeasureFirstItem,
+ ItemsLayout = new LinearItemsLayout(ItemsLayoutOrientation.Vertical) { ItemSpacing = 4 },
+ ItemTemplate = new DataTemplate(() => new MeasureFirstItemProbeCell()),
+ ItemsSource = CreateItems()
+ };
+ }
+
+ static List CreateItems()
+ {
+ var items = new List();
+ for (int index = 0; index < 80; index++)
+ {
+ items.Add(new MeasureFirstItemProbeItem(index));
+ }
+
+ return items;
+ }
+
+ void ResetProof()
+ {
+ MeasureFirstItemProbeRegistry.Reset();
+
+ _collectionView.ItemsSource = null;
+ _collectionView.ItemsSource = CreateItems();
+ _collectionView.ScrollTo(0, position: ScrollToPosition.Start, animate: false);
+
+ UpdateSummary();
+ }
+
+ void ScrollToItem40()
+ {
+ _collectionView.ScrollTo(40, position: ScrollToPosition.Start, animate: false);
+ }
+
+ protected override void OnDisappearing()
+ {
+ MeasureFirstItemProbeRegistry.MeasurementsChanged -= OnMeasurementsChanged;
+ base.OnDisappearing();
+ }
+
+ void OnMeasurementsChanged()
+ {
+ Dispatcher.Dispatch(UpdateSummary);
+ }
+
+ void UpdateSummary()
+ {
+ _summaryLabel.Text = MeasureFirstItemProbeRegistry.GetSummary();
+ }
+}
+
+public sealed class MeasureFirstItemProbeItem
+{
+ public MeasureFirstItemProbeItem(int index)
+ {
+ Index = index;
+ Title = $"Item {Index}";
+ }
+
+ public int Index { get; }
+
+ public string Title { get; }
+}
+
+static class MeasureFirstItemProbeRegistry
+{
+ static readonly object Lock = new();
+ static readonly Dictionary Records = new();
+
+ public static event Action MeasurementsChanged;
+
+ public static void Reset()
+ {
+ lock (Lock)
+ {
+ Records.Clear();
+ }
+
+ MeasurementsChanged?.Invoke();
+ }
+
+ public static void RecordMeasurement(MeasureFirstItemProbeItem item, double heightConstraint, double measuredHeight)
+ {
+ lock (Lock)
+ {
+ if (!Records.TryGetValue(item.Index, out var record))
+ {
+ record = new MeasureFirstItemProbeRecord(item);
+ Records[item.Index] = record;
+ }
+
+ record.MeasureCount++;
+ record.HeightConstraints.Add(heightConstraint);
+
+ if (item.Index == 0 && record.MeasuredHeight <= 0 && measuredHeight > 0)
+ {
+ record.MeasuredHeight = measuredHeight;
+ }
+ }
+
+ MeasurementsChanged?.Invoke();
+ }
+
+ public static string GetSummary()
+ {
+ lock (Lock)
+ {
+ var firstMeasuredHeight = GetFirstMeasuredHeight();
+ var cachedHeightMeasuredNonFirst = 0;
+
+ foreach (var record in Records.Values)
+ {
+ if (record.Index == 0 || record.MeasureCount == 0)
+ {
+ continue;
+ }
+
+ if (HasCachedHeightMeasure(record, firstMeasuredHeight))
+ {
+ cachedHeightMeasuredNonFirst++;
+ }
+ }
+
+ return $"Items2 CV2: {cachedHeightMeasuredNonFirst} cached-height non-first";
+ }
+ }
+
+ static double GetFirstMeasuredHeight()
+ {
+ foreach (var record in Records.Values)
+ {
+ if (record.Index == 0 && record.MeasuredHeight > 0)
+ {
+ return record.MeasuredHeight;
+ }
+ }
+
+ return 0;
+ }
+
+ static bool HasCachedHeightMeasure(MeasureFirstItemProbeRecord record, double firstHeight)
+ {
+ if (firstHeight <= 0)
+ {
+ return false;
+ }
+
+ foreach (var heightConstraint in record.HeightConstraints)
+ {
+ if (!double.IsInfinity(heightConstraint) && Math.Abs(heightConstraint - firstHeight) < 0.5)
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ sealed class MeasureFirstItemProbeRecord
+ {
+ public MeasureFirstItemProbeRecord(MeasureFirstItemProbeItem item)
+ {
+ Item = item;
+ }
+
+ public MeasureFirstItemProbeItem Item { get; }
+
+ public int Index => Item.Index;
+
+ public int MeasureCount { get; set; }
+
+ public double MeasuredHeight { get; set; }
+
+ public List HeightConstraints { get; } = new();
+ }
+}
+
+sealed class MeasureFirstItemProbeCell : Grid
+{
+ MeasureFirstItemProbeItem _item;
+
+ public MeasureFirstItemProbeCell()
+ {
+ Padding = new Thickness(12, 8);
+ var titleLabel = new Label
+ {
+ VerticalOptions = LayoutOptions.Center
+ };
+ titleLabel.SetBinding(Label.TextProperty, nameof(MeasureFirstItemProbeItem.Title));
+ Children.Add(titleLabel);
+ }
+
+ protected override void OnBindingContextChanged()
+ {
+ base.OnBindingContextChanged();
+ _item = BindingContext as MeasureFirstItemProbeItem;
+ }
+
+ protected override Size MeasureOverride(double widthConstraint, double heightConstraint)
+ {
+ var size = base.MeasureOverride(widthConstraint, heightConstraint);
+
+ if (_item is not null)
+ {
+ MeasureFirstItemProbeRegistry.RecordMeasurement(_item, heightConstraint, size.Height);
+ }
+
+ return size;
+ }
+}
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue35902.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue35902.cs
new file mode 100644
index 000000000000..a3fe8510cf01
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue35902.cs
@@ -0,0 +1,69 @@
+namespace Maui.Controls.Sample.Issues;
+
+[Issue(IssueTracker.Github, 35902, "[iOS] Transparent Shell Navigation Bar Breaks After Keyboard Interaction on Secondary Pages", PlatformAffected.iOS)]
+public class Issue35902 : TestShell
+{
+ public Issue35902()
+ {
+ Shell.SetBackgroundColor(this, Colors.Transparent);
+ Routing.RegisterRoute(nameof(Issue35902SecondPage), typeof(Issue35902SecondPage));
+ }
+
+ protected override void Init()
+ {
+ AddContentPage(new Issue35902MainPage());
+ }
+}
+
+public class Issue35902MainPage : ContentPage
+{
+ public Issue35902MainPage()
+ {
+ Title = "Issue 35902";
+
+ var navigateButton = new Button
+ {
+ Text = "Navigate to Second Page",
+ AutomationId = "NavigateButton",
+ HorizontalOptions = LayoutOptions.Center,
+ VerticalOptions = LayoutOptions.Center
+ };
+
+ navigateButton.Clicked += async (s, e) =>
+ {
+ await Shell.Current.GoToAsync(nameof(Issue35902SecondPage));
+ };
+
+ Content = new VerticalStackLayout
+ {
+ VerticalOptions = LayoutOptions.Center,
+ Children = { navigateButton }
+ };
+ }
+}
+
+public class Issue35902SecondPage : ContentPage
+{
+ public Issue35902SecondPage()
+ {
+ Title = "Second Page";
+ BackgroundColor = Colors.LightSkyBlue;
+
+ var entry = new Entry
+ {
+ Placeholder = "Tap here to show keyboard",
+ AutomationId = "TestEntry"
+ };
+
+ Content = new VerticalStackLayout
+ {
+ Padding = new Thickness(20),
+ VerticalOptions = LayoutOptions.End,
+ Children =
+ {
+ new Label { Text = "Tap Entry, then dismiss keyboard" },
+ entry
+ }
+ };
+ }
+}
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue35943.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue35943.cs
new file mode 100644
index 000000000000..278c167ba59f
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue35943.cs
@@ -0,0 +1,98 @@
+namespace Maui.Controls.Sample.Issues;
+
+[Issue(IssueTracker.Github, 35943, "[iOS, MacCatalyst] GetPosition Truncates Fractional Coordinates to Integers on TappedEvent", PlatformAffected.iOS)]
+public class Issue35943 : ContentPage
+{
+ public Issue35943()
+ {
+ // A half-point left/top margin places this view at a fractional UIKit position (X=0.5, Y=0.5
+ // relative to the container). Any tap at an integer screen coordinate will therefore produce
+ // fractional coordinates when expressed in this view's local coordinate system.
+ var referenceBox = new BoxView
+ {
+ Color = Colors.CornflowerBlue,
+ WidthRequest = 10,
+ HeightRequest = 10,
+ HorizontalOptions = LayoutOptions.Start,
+ Margin = new Thickness(0.5, 0.5, 0, 0),
+ AutomationId = "ReferenceBox"
+ };
+
+ var instructionLabel = new Label
+ {
+ Text = "Tap the red box. Coordinates relative to the blue box should be fractional.",
+ AutomationId = "InstructionLabel"
+ };
+
+ // resultLabel shows human-readable output; statusLabel holds the AutomationId the test waits for.
+ // They are separate because AutomationId may only be set once on iOS/MacCatalyst.
+ var resultLabel = new Label
+ {
+ Text = "Tap the red box",
+ AutomationId = "ResultLabel"
+ };
+
+ var statusLabel = new Label { Text = string.Empty };
+
+ var tapTarget = new BoxView
+ {
+ Color = Colors.Tomato,
+ WidthRequest = 200,
+ HeightRequest = 200,
+ HorizontalOptions = LayoutOptions.Start,
+ AutomationId = "TapTarget"
+ };
+
+ var tapGesture = new TapGestureRecognizer();
+ tapGesture.Tapped += (s, e) =>
+ {
+ var position = e.GetPosition(relativeTo: referenceBox);
+
+ if (position is null)
+ {
+ resultLabel.Text = "Failure: position is null";
+ if (string.IsNullOrEmpty(statusLabel.AutomationId))
+ statusLabel.AutomationId = "Failure";
+ statusLabel.Text = "Failure";
+ return;
+ }
+
+ // Because referenceBox is at a fractional UIKit position (Margin = 0.5),
+ // GetPosition(relativeTo: referenceBox) should return coordinates with
+ // a fractional component. Before the fix, the (int) cast in CalculatePosition
+ // would truncate e.g. 99.5 → 99, losing sub-pixel precision.
+ double fracX = Math.Abs(position.Value.X - Math.Truncate(position.Value.X));
+ double fracY = Math.Abs(position.Value.Y - Math.Truncate(position.Value.Y));
+ bool hasFractionalPrecision = fracX > 0.01 || fracY > 0.01;
+
+ if (hasFractionalPrecision)
+ {
+ resultLabel.Text = $"Success: X={position.Value.X:F4}, Y={position.Value.Y:F4}";
+ if (string.IsNullOrEmpty(statusLabel.AutomationId))
+ statusLabel.AutomationId = "Success";
+ statusLabel.Text = "Success";
+ }
+ else
+ {
+ resultLabel.Text = $"Failure: X={position.Value.X}, Y={position.Value.Y} (expected fractional coordinates)";
+ if (string.IsNullOrEmpty(statusLabel.AutomationId))
+ statusLabel.AutomationId = "Failure";
+ statusLabel.Text = "Failure";
+ }
+ };
+
+ tapTarget.GestureRecognizers.Add(tapGesture);
+
+ Content = new VerticalStackLayout
+ {
+ Children =
+ {
+ instructionLabel,
+ referenceBox,
+ tapTarget,
+ resultLabel,
+ statusLabel
+ }
+ };
+ }
+}
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue36154.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue36154.cs
index 6d389e571fa8..3d70aaac755e 100644
--- a/src/Controls/tests/TestCases.HostApp/Issues/Issue36154.cs
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue36154.cs
@@ -97,7 +97,16 @@ public Issue36154()
directionLabel.Text = $"Direction: {e.SwipeDirection} Open: {e.IsOpen}";
};
- Content = new Grid
+ var headerLabel = new Label
+ {
+ Text = "Swipe on WebView · scroll mid-page · swipe at edges",
+ HorizontalOptions = LayoutOptions.Center,
+ Margin = new Thickness(8),
+ FontSize = 13,
+ FontAttributes = FontAttributes.Bold
+ };
+
+ var grid = new Grid
{
RowDefinitions =
[
@@ -105,22 +114,14 @@ public Issue36154()
new RowDefinition { Height = GridLength.Star },
new RowDefinition { Height = GridLength.Auto },
new RowDefinition { Height = GridLength.Auto },
- ],
- Children =
- {
- new Label
- {
- Text = "Swipe on WebView · scroll mid-page · swipe at edges",
- HorizontalOptions = LayoutOptions.Center,
- Margin = new Thickness(8),
- FontSize = 13,
- FontAttributes = FontAttributes.Bold
- }.Row(0),
-
- swipeView.Row(1),
- directionLabel.Row(2),
- resultLabel.Row(3)
- }
+ ]
};
+
+ grid.Add(headerLabel, 0, 0);
+ grid.Add(swipeView, 0, 1);
+ grid.Add(directionLabel, 0, 2);
+ grid.Add(resultLabel, 0, 3);
+
+ Content = grid;
}
}
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue36853.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue36853.cs
new file mode 100644
index 000000000000..463f48e5cbc5
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue36853.cs
@@ -0,0 +1,111 @@
+namespace Maui.Controls.Sample.Issues;
+
+// Reproduces the bug: Root → SecondPage (singleton) → ThirdPage → ///Root → SecondPage again → BLANK
+[Issue(IssueTracker.Github, 36853, "Shell singleton page renders blank when re-pushed after absolute route PopToRoot on Android", PlatformAffected.Android)]
+public class Issue36853 : TestShell
+{
+ protected override void Init()
+ {
+ Routing.RegisterRoute("Issue36853Second", typeof(Issue36853SecondPage));
+ Routing.RegisterRoute("Issue36853Third", typeof(Issue36853ThirdPage));
+
+ var mainPage = new ContentPage
+ {
+ Title = "Main",
+ Content = new VerticalStackLayout
+ {
+ Spacing = 20,
+ Padding = 20,
+ Children =
+ {
+ new Label
+ {
+ Text = "Main Page",
+ AutomationId = "Issue36853MainLabel",
+ FontSize = 24
+ },
+ new Button
+ {
+ Text = "Go to Second Page",
+ AutomationId = "Issue36853GoToSecond",
+ Command = new Command(async () =>
+ await Shell.Current.GoToAsync("Issue36853Second"))
+ }
+ }
+ }
+ };
+
+ AddContentPage(mainPage, "Issue36853Main");
+ }
+}
+
+// Registered as Singleton in DI — same instance returned every time the route resolves
+public class Issue36853SecondPage : ContentPage
+{
+ public Issue36853SecondPage()
+ {
+ Title = "Second";
+ Content = new VerticalStackLayout
+ {
+ Spacing = 20,
+ Padding = 20,
+ Children =
+ {
+ new Label
+ {
+ Text = "Second Page Content",
+ AutomationId = "Issue36853SecondLabel",
+ FontSize = 24
+ },
+ new Button
+ {
+ Text = "Go to Third Page",
+ AutomationId = "Issue36853GoToThird",
+ Command = new Command(async () =>
+ await Shell.Current.GoToAsync("Issue36853Third"))
+ }
+ }
+ };
+ }
+}
+
+public class Issue36853ThirdPage : ContentPage
+{
+ public Issue36853ThirdPage()
+ {
+ Title = "Third";
+ Content = new VerticalStackLayout
+ {
+ Spacing = 20,
+ Padding = 20,
+ Children =
+ {
+ new Label
+ {
+ Text = "Third Page Content",
+ AutomationId = "Issue36853ThirdLabel",
+ FontSize = 24
+ },
+ new Button
+ {
+ Text = "Reset to Root (///)",
+ AutomationId = "Issue36853ResetToRoot",
+ Command = new Command(async () =>
+ await Shell.Current.GoToAsync("///Issue36853Main"))
+ }
+ }
+ };
+ }
+}
+
+static class Issue36853Extensions
+{
+ public static MauiAppBuilder Issue36853RegisterServices(this MauiAppBuilder builder)
+ {
+ // SecondPage is singleton — same instance reused across navigations (the bug scenario)
+ builder.Services.AddSingleton();
+ // ThirdPage is transient — new instance each time (normal behavior)
+ builder.Services.AddTransient();
+ return builder;
+ }
+}
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue36942.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue36942.cs
new file mode 100644
index 000000000000..b1f8d477e835
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue36942.cs
@@ -0,0 +1,96 @@
+using Microsoft.Maui.Controls;
+using Microsoft.Maui.Controls.Shapes;
+using Microsoft.Maui.Graphics;
+
+namespace Maui.Controls.Sample.Issues
+{
+ [Issue(IssueTracker.Github, 36942, "Border with Shadow breaks descendant BackgroundColor UI updates on Android", PlatformAffected.Android)]
+ public class Issue36942 : ContentPage
+ {
+ static readonly Color ActivatedColor = Colors.DodgerBlue;
+ static readonly Color DefaultColor = Color.FromArgb("#FFF5F5F5");
+
+ readonly Border _toggleTarget;
+ readonly Label _viewModelStateLabel;
+
+ bool _activated;
+
+ public Issue36942()
+ {
+ AutomationId = "Issue36942Page";
+ Title = "Issue 36942";
+ BackgroundColor = Colors.White;
+
+ _toggleTarget = new Border
+ {
+ AutomationId = "ToggleTarget",
+ Stroke = Colors.Transparent,
+ StrokeShape = new RoundRectangle { CornerRadius = 20 },
+ BackgroundColor = DefaultColor,
+ Padding = new Thickness(20, 10),
+ WidthRequest = 220,
+ HorizontalOptions = LayoutOptions.Center,
+ Content = new Label
+ {
+ Text = "Tap to toggle",
+ TextColor = Color.FromArgb("#333333"),
+ HorizontalOptions = LayoutOptions.Center,
+ VerticalOptions = LayoutOptions.Center,
+ },
+ };
+
+ var tap = new TapGestureRecognizer();
+ tap.Tapped += OnToggleTapped;
+ _toggleTarget.GestureRecognizers.Add(tap);
+
+ var outerBorder = new Border
+ {
+ Stroke = Colors.Transparent,
+ StrokeShape = new RoundRectangle { CornerRadius = 20 },
+ BackgroundColor = Color.FromArgb("#222222"),
+ Padding = 20,
+ HorizontalOptions = LayoutOptions.Center,
+ VerticalOptions = LayoutOptions.Center,
+ Shadow = new Shadow
+ {
+ Brush = Brush.Black,
+ Offset = new Point(20, 20),
+ Radius = 40,
+ Opacity = 0.8f,
+ },
+ Content = _toggleTarget,
+ };
+
+ _viewModelStateLabel = new Label
+ {
+ AutomationId = "ViewModelState",
+ Text = "Activated: False",
+ FontSize = 16,
+ Margin = new Thickness(0, 40, 0, 0),
+ HorizontalOptions = LayoutOptions.Center,
+ };
+
+ var grid = new Grid
+ {
+ Padding = 40,
+ RowDefinitions =
+ {
+ new RowDefinition { Height = GridLength.Star },
+ new RowDefinition { Height = GridLength.Auto },
+ new RowDefinition { Height = GridLength.Auto },
+ new RowDefinition { Height = GridLength.Star },
+ },
+ };
+ grid.Add(outerBorder, 0, 1);
+ grid.Add(_viewModelStateLabel, 0, 2);
+
+ Content = grid;
+ }
+ void OnToggleTapped(object sender, TappedEventArgs e)
+ {
+ _activated = !_activated;
+ _toggleTarget.BackgroundColor = _activated ? ActivatedColor : DefaultColor;
+ _viewModelStateLabel.Text = $"Activated: {_activated}";
+ }
+ }
+}
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue4715.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue4715.cs
index 6159be608f74..eb0b4f167227 100644
--- a/src/Controls/tests/TestCases.HostApp/Issues/Issue4715.cs
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue4715.cs
@@ -5,6 +5,114 @@ public class Issue4715 : ContentPage
{
public Issue4715()
{
+ // Grid with AutomationId only. This verifies UI tests can find layout containers
+ // by AutomationId without needing explicit accessible-tree opt-in.
+ var testGrid = new Grid
+ {
+ AutomationId = "TestGrid",
+ BackgroundColor = Colors.LightBlue,
+ HeightRequest = 60,
+ Children =
+ {
+ new Label { Text = "Grid", VerticalOptions = LayoutOptions.Center, HorizontalOptions = LayoutOptions.Center }
+ }
+ };
+
+ // VerticalStackLayout with AutomationId and explicit accessible-tree opt-in.
+ var testVerticalStackLayout = new VerticalStackLayout
+ {
+ AutomationId = "TestVerticalStackLayout",
+ BackgroundColor = Colors.LightGreen,
+ Children =
+ {
+ new Label { Text = "VerticalStackLayout", Padding = new Thickness(8) }
+ }
+ };
+ SemanticProperties.SetDescription(testVerticalStackLayout, "Test vertical stack layout");
+ AutomationProperties.SetIsInAccessibleTree(testVerticalStackLayout, true);
+
+ // HorizontalStackLayout with AutomationId and explicit accessible-tree opt-in.
+ var testHorizontalStackLayout = new HorizontalStackLayout
+ {
+ AutomationId = "TestHorizontalStackLayout",
+ BackgroundColor = Colors.LightYellow,
+ Children =
+ {
+ new Label { Text = "HorizontalStackLayout", Padding = new Thickness(8) }
+ }
+ };
+ SemanticProperties.SetDescription(testHorizontalStackLayout, "Test horizontal stack layout");
+ AutomationProperties.SetIsInAccessibleTree(testHorizontalStackLayout, true);
+
+ // FlexLayout with AutomationId and explicit accessible-tree opt-in.
+ var testFlexLayout = new FlexLayout
+ {
+ AutomationId = "TestFlexLayout",
+ BackgroundColor = Colors.LightPink,
+ HeightRequest = 60,
+ Children =
+ {
+ new Label { Text = "FlexLayout", Margin = new Thickness(8) }
+ }
+ };
+ SemanticProperties.SetDescription(testFlexLayout, "Test flex layout");
+ AutomationProperties.SetIsInAccessibleTree(testFlexLayout, true);
+
+ // AbsoluteLayout with AutomationId and explicit accessible-tree opt-in.
+ var testAbsoluteLayout = new AbsoluteLayout
+ {
+ AutomationId = "TestAbsoluteLayout",
+ BackgroundColor = Colors.LightSteelBlue,
+ HeightRequest = 60,
+ Children =
+ {
+ new Label
+ {
+ Text = "AbsoluteLayout",
+ Margin = new Thickness(8)
+ }
+ }
+ };
+ SemanticProperties.SetDescription(testAbsoluteLayout, "Test absolute layout");
+ AutomationProperties.SetIsInAccessibleTree(testAbsoluteLayout, true);
+
+ // Nested layout — outer has AutomationId and explicit accessible-tree opt-in, inner is anonymous.
+ var testNestedOuterGrid = new Grid
+ {
+ AutomationId = "TestNestedOuterGrid",
+ BackgroundColor = Colors.Lavender,
+ HeightRequest = 80,
+ Children =
+ {
+ new VerticalStackLayout
+ {
+ // No AutomationId — anonymous inner layout
+ Children =
+ {
+ new Label { Text = "Nested: Outer Grid (named)", HorizontalOptions = LayoutOptions.Center },
+ new Label { Text = "Inner VerticalStackLayout (anonymous)", HorizontalOptions = LayoutOptions.Center, FontSize = 11 }
+ }
+ }
+ }
+ };
+ SemanticProperties.SetDescription(testNestedOuterGrid, "Test nested outer grid layout");
+ AutomationProperties.SetIsInAccessibleTree(testNestedOuterGrid, true);
+
+ // Layout with an AutomationId but an explicit accessible-tree opt-out (IsInAccessibleTree="False").
+ // The Raw opt-out removes it from the UIA Control view, so Appium must NOT be able to find it by
+ // its AutomationId — proving the explicit opt-out takes precedence over the AutomationId test hook.
+ var testOptedOutGrid = new Grid
+ {
+ AutomationId = "OptedOutGrid",
+ BackgroundColor = Colors.LightGray,
+ HeightRequest = 60,
+ Children =
+ {
+ new Label { Text = "Opted-out Grid (IsInAccessibleTree=False)", VerticalOptions = LayoutOptions.Center, HorizontalOptions = LayoutOptions.Center }
+ }
+ };
+ AutomationProperties.SetIsInAccessibleTree(testOptedOutGrid, false);
+
var scrollView = new ScrollView
{
Content = new VerticalStackLayout
@@ -20,99 +128,13 @@ public Issue4715()
FontAttributes = FontAttributes.Bold,
AutomationId = "PageTitle"
},
-
- // Grid with AutomationId
- new Grid
- {
- AutomationId = "TestGrid",
- BackgroundColor = Colors.LightBlue,
- HeightRequest = 60,
- Children =
- {
- new Label { Text = "Grid", VerticalOptions = LayoutOptions.Center, HorizontalOptions = LayoutOptions.Center }
- }
- },
-
- // VerticalStackLayout with AutomationId
- new VerticalStackLayout
- {
- AutomationId = "TestVerticalStackLayout",
- BackgroundColor = Colors.LightGreen,
- Children =
- {
- new Label { Text = "VerticalStackLayout", Padding = new Thickness(8) }
- }
- },
-
- // HorizontalStackLayout with AutomationId
- new HorizontalStackLayout
- {
- AutomationId = "TestHorizontalStackLayout",
- BackgroundColor = Colors.LightYellow,
- Children =
- {
- new Label { Text = "HorizontalStackLayout", Padding = new Thickness(8) }
- }
- },
-
- // FlexLayout with AutomationId
- new FlexLayout
- {
- AutomationId = "TestFlexLayout",
- BackgroundColor = Colors.LightPink,
- HeightRequest = 60,
- Children =
- {
- new Label { Text = "FlexLayout", Margin = new Thickness(8) }
- }
- },
-
- // AbsoluteLayout with AutomationId
- new AbsoluteLayout
- {
- AutomationId = "TestAbsoluteLayout",
- BackgroundColor = Colors.LightSteelBlue,
- HeightRequest = 60,
- Children =
- {
- new Label
- {
- Text = "AbsoluteLayout",
- Margin = new Thickness(8)
- }
- }
- },
-
- // Nested layout — outer has AutomationId, inner is anonymous
- new Grid
- {
- AutomationId = "TestNestedOuterGrid",
- BackgroundColor = Colors.Lavender,
- HeightRequest = 80,
- Children =
- {
- new VerticalStackLayout
- {
- // No AutomationId — anonymous inner layout
- Children =
- {
- new Label { Text = "Nested: Outer Grid (named)", HorizontalOptions = LayoutOptions.Center },
- new Label { Text = "Inner VerticalStackLayout (anonymous)", HorizontalOptions = LayoutOptions.Center, FontSize = 11 }
- }
- }
- }
- },
-
- // Anonymous layout — no AutomationId, should NOT be found by Appium
- new Grid
- {
- BackgroundColor = Colors.LightGray,
- HeightRequest = 60,
- Children =
- {
- new Label { Text = "Anonymous Grid (no AutomationId)", VerticalOptions = LayoutOptions.Center, HorizontalOptions = LayoutOptions.Center }
- }
- },
+ testGrid,
+ testVerticalStackLayout,
+ testHorizontalStackLayout,
+ testFlexLayout,
+ testAbsoluteLayout,
+ testNestedOuterGrid,
+ testOptedOutGrid,
// Sentinel label to confirm page has loaded
new Label
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue6016.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue6016.cs
new file mode 100644
index 000000000000..732c9c575c87
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue6016.cs
@@ -0,0 +1,184 @@
+namespace Maui.Controls.Sample.Issues;
+
+[Issue(IssueTracker.Github, 6016, "SwipeView Threshold changes width and offset of the side menu", PlatformAffected.Android | PlatformAffected.iOS)]
+public class Issue6016 : ContentPage
+{
+ public Issue6016()
+ {
+ var defaultSwipeItem = new SwipeItem
+ {
+ Text = "Action",
+ BackgroundColor = Colors.LightBlue,
+ AutomationId = "DefaultSwipeItem"
+ };
+
+ var defaultSwipeView = new SwipeView
+ {
+ LeftItems = new SwipeItems { defaultSwipeItem },
+ AutomationId = "DefaultSwipeView",
+ Content = new Grid
+ {
+ HeightRequest = 60,
+ BackgroundColor = Colors.LightGray,
+ Children =
+ {
+ new Label
+ {
+ Text = "No Threshold",
+ HorizontalOptions = LayoutOptions.Center,
+ VerticalOptions = LayoutOptions.Center,
+ AutomationId = "DefaultContent"
+ }
+ }
+ }
+ };
+
+ var thresholdSwipeItem = new SwipeItem
+ {
+ Text = "Action",
+ BackgroundColor = Colors.LightBlue,
+ AutomationId = "ThresholdSwipeItem"
+ };
+
+ // Threshold = 200 should NOT affect menu width (it should stay at default 100)
+ var thresholdSwipeView = new SwipeView
+ {
+ LeftItems = new SwipeItems { thresholdSwipeItem },
+ Threshold = 200,
+ AutomationId = "ThresholdSwipeView",
+ Content = new Grid
+ {
+ HeightRequest = 60,
+ BackgroundColor = Colors.LightGray,
+ Children =
+ {
+ new Label
+ {
+ Text = "Threshold 200",
+ HorizontalOptions = LayoutOptions.Center,
+ VerticalOptions = LayoutOptions.Center,
+ AutomationId = "ThresholdContent"
+ }
+ }
+ }
+ };
+
+ var defaultRightSwipeItem = new SwipeItem
+ {
+ Text = "Action",
+ BackgroundColor = Colors.LightGreen,
+ AutomationId = "DefaultRightSwipeItem"
+ };
+
+ var defaultRightSwipeView = new SwipeView
+ {
+ RightItems = new SwipeItems { defaultRightSwipeItem },
+ AutomationId = "DefaultRightSwipeView",
+ Content = new Grid
+ {
+ HeightRequest = 60,
+ BackgroundColor = Colors.LightGray,
+ Children =
+ {
+ new Label
+ {
+ Text = "No Threshold (Right)",
+ HorizontalOptions = LayoutOptions.Center,
+ VerticalOptions = LayoutOptions.Center,
+ AutomationId = "DefaultRightContent"
+ }
+ }
+ }
+ };
+
+ var thresholdRightSwipeItem = new SwipeItem
+ {
+ Text = "Action",
+ BackgroundColor = Colors.LightGreen,
+ AutomationId = "ThresholdRightSwipeItem"
+ };
+
+ // Threshold = 200 should NOT affect right menu width (it should stay at default 100)
+ var thresholdRightSwipeView = new SwipeView
+ {
+ RightItems = new SwipeItems { thresholdRightSwipeItem },
+ Threshold = 200,
+ AutomationId = "ThresholdRightSwipeView",
+ Content = new Grid
+ {
+ HeightRequest = 60,
+ BackgroundColor = Colors.LightGray,
+ Children =
+ {
+ new Label
+ {
+ Text = "Threshold 200 (Right)",
+ HorizontalOptions = LayoutOptions.Center,
+ VerticalOptions = LayoutOptions.Center,
+ AutomationId = "ThresholdRightContent"
+ }
+ }
+ }
+ };
+
+ var executeResultLabel = new Label
+ {
+ Text = "Not Executed",
+ AutomationId = "ExecuteResultLabel"
+ };
+
+ var executeSwipeItem = new SwipeItem
+ {
+ Text = "Execute",
+ BackgroundColor = Colors.OrangeRed,
+ AutomationId = "ExecuteSwipeItem"
+ };
+ executeSwipeItem.Invoked += (s, e) => executeResultLabel.Text = "Executed";
+
+ var executeItems = new SwipeItems { Mode = SwipeMode.Execute };
+ executeItems.Add(executeSwipeItem);
+
+ var executeSwipeView = new SwipeView
+ {
+ LeftItems = executeItems,
+ AutomationId = "ExecuteSwipeView",
+ Content = new Grid
+ {
+ HeightRequest = 60,
+ BackgroundColor = Colors.LightYellow,
+ Children =
+ {
+ new Label
+ {
+ Text = "Swipe right to execute",
+ HorizontalOptions = LayoutOptions.Center,
+ VerticalOptions = LayoutOptions.Center,
+ AutomationId = "ExecuteContent"
+ }
+ }
+ }
+ };
+
+ Content = new ScrollView
+ {
+ Content = new VerticalStackLayout
+ {
+ Spacing = 20,
+ Padding = new Thickness(20),
+ Children =
+ {
+ new Label { Text = "SwipeView Threshold Test", FontSize = 16, FontAttributes = FontAttributes.Bold },
+ new Label { Text = "Both rows should show same-width menu when opened" },
+ defaultSwipeView,
+ thresholdSwipeView,
+ new Label { Text = "Right swipe items (swipe left to open):" },
+ defaultRightSwipeView,
+ thresholdRightSwipeView,
+ new Label { Text = "Execute mode (swipe right past 80% of width):" },
+ executeSwipeView,
+ executeResultLabel
+ }
+ }
+ };
+ }
+}
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue7580.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue7580.cs
index 79b027dfcd9b..3e57073036c0 100644
--- a/src/Controls/tests/TestCases.HostApp/Issues/Issue7580.cs
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue7580.cs
@@ -64,6 +64,7 @@ public Issue7580()
{
HeightRequest = 60,
BackgroundColor = Colors.LightGray,
+ AutomationId = "SwipeContent"
};
swipeContent.Add(new Label
{
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue7814.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue7814.cs
index a9b4672a4192..4fc67f5445d8 100644
--- a/src/Controls/tests/TestCases.HostApp/Issues/Issue7814.cs
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue7814.cs
@@ -1,3 +1,8 @@
+#if ANDROID
+using Microsoft.Maui.Handlers;
+using AView = Android.Views.View;
+#endif
+
namespace Maui.Controls.Sample.Issues;
[Issue(IssueTracker.Github, 7814, "Vertical scrolling not working for CarouselView and CustomLayouts", PlatformAffected.Android)]
@@ -5,14 +10,28 @@ public class Issue7814 : TestContentPage
{
const string VerticalOffsetPrefix = "VerticalScrollY";
const string HorizontalOffsetPrefix = "HorizontalScrollX";
+#if ANDROID
+ const string TouchParentPositionPrefix = "TouchParentPosition";
+ const string TouchStatusPrefix = "TouchStatus";
+ const string TouchClaimViewId = "Issue7814TouchClaimView";
+ const string TouchReleaseViewId = "Issue7814TouchReleaseView";
+#endif
Label _verticalOffsetLabel = null!;
Label _horizontalOffsetLabel = null!;
+#if ANDROID
+ Label _touchParentPositionLabel = null!;
+ Label _touchStatusLabel = null!;
+#endif
protected override void Init()
{
_verticalOffsetLabel = CreateOffsetLabel("Issue7814VerticalScrollYLabel", VerticalOffsetPrefix);
_horizontalOffsetLabel = CreateOffsetLabel("Issue7814HorizontalScrollXLabel", HorizontalOffsetPrefix);
+#if ANDROID
+ _touchParentPositionLabel = CreateOffsetLabel("Issue7814TouchParentPositionLabel", TouchParentPositionPrefix);
+ _touchStatusLabel = CreateOffsetLabel("Issue7814TouchStatusLabel", TouchStatusPrefix);
+#endif
Grid.SetColumn(_horizontalOffsetLabel, 1);
@@ -37,6 +56,13 @@ protected override void Init()
new Grid
{
Padding = new Thickness(12, 8),
+ RowDefinitions =
+ {
+ new RowDefinition(GridLength.Auto),
+#if ANDROID
+ new RowDefinition(GridLength.Auto)
+#endif
+ },
ColumnDefinitions =
{
new ColumnDefinition(GridLength.Star),
@@ -45,7 +71,11 @@ protected override void Init()
Children =
{
_verticalOffsetLabel,
- _horizontalOffsetLabel
+ Column(_horizontalOffsetLabel, 1),
+#if ANDROID
+ Row(_touchParentPositionLabel, 1),
+ Column(Row(_touchStatusLabel, 1), 1)
+#endif
}
},
outerScrollView
@@ -115,6 +145,10 @@ View CreateScrollableContent()
};
horizontalScrollView.Scrolled += (_, e) => UpdateOffset(_horizontalOffsetLabel, HorizontalOffsetPrefix, e.ScrollX);
+#if ANDROID
+ var touchClaimRegressionParent = CreateTouchClaimRegressionParent();
+#endif
+
return new VerticalStackLayout
{
Spacing = 16,
@@ -134,6 +168,15 @@ View CreateScrollableContent()
Margin = new Thickness(12, 0)
},
horizontalScrollView,
+#if ANDROID
+ new Label
+ {
+ Text = "Touch-claiming row in a vertical CollectionView inside a horizontal parent",
+ FontSize = 20,
+ Margin = new Thickness(12, 0)
+ },
+ touchClaimRegressionParent,
+#endif
new BoxView
{
HeightRequest = 900,
@@ -143,6 +186,103 @@ View CreateScrollableContent()
};
}
+#if ANDROID
+ View CreateTouchClaimRegressionParent()
+ {
+ var carouselView = new CarouselView
+ {
+ AutomationId = "Issue7814TouchClaimHorizontalParent",
+ HeightRequest = 380,
+ Loop = false,
+ ItemsLayout = new LinearItemsLayout(ItemsLayoutOrientation.Horizontal),
+ ItemsSource = Enumerable.Range(1, 3).ToList(),
+ ItemTemplate = new DataTemplate(CreateTouchClaimRegressionCarouselItem)
+ };
+ carouselView.PositionChanged += (_, e) => UpdateOffset(_touchParentPositionLabel, TouchParentPositionPrefix, e.CurrentPosition);
+
+ return carouselView;
+ }
+
+ View CreateTouchClaimRegressionCarouselItem()
+ {
+ var collectionView = new CollectionView
+ {
+ AutomationId = "Issue7814TouchClaimCollectionView",
+ WidthRequest = 360,
+ HeightRequest = 360,
+ ItemsLayout = new LinearItemsLayout(ItemsLayoutOrientation.Vertical),
+ ItemsSource = CreateTouchClaimRows(),
+ ItemTemplate = new DataTemplate(CreateTouchClaimRow)
+ };
+
+ return new Grid
+ {
+ Children =
+ {
+ collectionView
+ }
+ };
+ }
+
+ View CreateTouchClaimRow()
+ {
+ var contentLabel = new Label
+ {
+ AutomationId = "Issue7814TouchClaimRowLabel",
+ FontSize = 18,
+ InputTransparent = true,
+ HorizontalOptions = LayoutOptions.Center,
+ VerticalOptions = LayoutOptions.Center
+ };
+ contentLabel.SetBinding(Label.TextProperty, nameof(TouchClaimRow.Text));
+
+ var touchClaimView = new Issue7814TouchClaimView
+ {
+ BackgroundColor = Colors.LightGreen,
+ TouchStateChanged = state => _touchStatusLabel.Text = state
+ };
+ touchClaimView.SetBinding(AutomationIdProperty, nameof(TouchClaimRow.AutomationId));
+ touchClaimView.SetBinding(Issue7814TouchClaimView.ReleaseTouchOwnershipOnMoveProperty, nameof(TouchClaimRow.ReleaseTouchOwnershipOnMove));
+
+ return new Grid
+ {
+ HeightRequest = 84,
+ Margin = new Thickness(8, 4),
+ BackgroundColor = Colors.LightBlue,
+ Children =
+ {
+ touchClaimView,
+ contentLabel
+ }
+ };
+ }
+
+ static List CreateTouchClaimRows()
+ {
+ var rows = new List
+ {
+ new("Touch row keeps ownership", TouchClaimViewId, false),
+ new("Touch row releases ownership", TouchReleaseViewId, true)
+ };
+
+ rows.AddRange(Enumerable.Range(3, 10).Select(index => new TouchClaimRow($"Touch row {index}", $"Issue7814TouchFillerView{index}", false)));
+
+ return rows;
+ }
+#endif
+
+ static T Row(T view, int row) where T : View
+ {
+ Grid.SetRow(view, row);
+ return view;
+ }
+
+ static T Column(T view, int column) where T : View
+ {
+ Grid.SetColumn(view, column);
+ return view;
+ }
+
static Label CreateOffsetLabel(string automationId, string prefix)
{
var label = new Label
@@ -160,3 +300,93 @@ static void UpdateOffset(Label label, string prefix, double offset)
label.Text = $"{prefix}: {(int)Math.Round(offset)}";
}
}
+
+#if ANDROID
+public record TouchClaimRow(string Text, string AutomationId, bool ReleaseTouchOwnershipOnMove);
+
+public class Issue7814TouchClaimView : View
+{
+ public static readonly BindableProperty ReleaseTouchOwnershipOnMoveProperty =
+ BindableProperty.Create(nameof(ReleaseTouchOwnershipOnMove), typeof(bool), typeof(Issue7814TouchClaimView), false);
+
+ public Action TouchStateChanged { get; set; }
+
+ public bool ReleaseTouchOwnershipOnMove
+ {
+ get => (bool)GetValue(ReleaseTouchOwnershipOnMoveProperty);
+ set => SetValue(ReleaseTouchOwnershipOnMoveProperty, value);
+ }
+
+ internal void SendTouchState(string state)
+ {
+ TouchStateChanged?.Invoke(state);
+ }
+}
+
+public class Issue7814TouchClaimViewHandler : ViewHandler
+{
+ int _moveCount;
+
+ public Issue7814TouchClaimViewHandler() : base(ViewHandler.ViewMapper, ViewHandler.ViewCommandMapper)
+ {
+ }
+
+ protected override AView CreatePlatformView()
+ {
+ return new AView(Context)
+ {
+ Clickable = true
+ };
+ }
+
+ protected override void ConnectHandler(AView platformView)
+ {
+ base.ConnectHandler(platformView);
+ platformView.Touch += OnTouch;
+ }
+
+ protected override void DisconnectHandler(AView platformView)
+ {
+ platformView.Touch -= OnTouch;
+ base.DisconnectHandler(platformView);
+ }
+
+ void OnTouch(object sender, AView.TouchEventArgs e)
+ {
+ if (e.Event is null)
+ {
+ return;
+ }
+
+ switch (e.Event.ActionMasked)
+ {
+ case Android.Views.MotionEventActions.Down:
+ _moveCount = 0;
+ RequestTouchOwnership();
+ VirtualView.SendTouchState("TouchStatus: Down");
+ e.Handled = true;
+ break;
+ case Android.Views.MotionEventActions.Move:
+ _moveCount++;
+ RequestTouchOwnership(!VirtualView.ReleaseTouchOwnershipOnMove);
+ VirtualView.SendTouchState($"TouchStatus: Move {_moveCount}");
+ e.Handled = true;
+ break;
+ case Android.Views.MotionEventActions.Up:
+ RequestTouchOwnership(!VirtualView.ReleaseTouchOwnershipOnMove);
+ VirtualView.SendTouchState($"TouchStatus: Up {_moveCount}");
+ e.Handled = true;
+ break;
+ case Android.Views.MotionEventActions.Cancel:
+ VirtualView.SendTouchState($"TouchStatus: Cancel {_moveCount}");
+ e.Handled = true;
+ break;
+ }
+ }
+
+ void RequestTouchOwnership(bool disallowIntercept = true)
+ {
+ PlatformView?.Parent?.RequestDisallowInterceptTouchEvent(disallowIntercept);
+ }
+}
+#endif
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue8680.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue8680.cs
new file mode 100644
index 000000000000..0546cb2f3a12
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue8680.cs
@@ -0,0 +1,66 @@
+namespace Maui.Controls.Sample.Issues;
+
+[Issue(IssueTracker.Github, 8680, "Rework OnBackButtonPressed to use onBackPressedDispatcher", PlatformAffected.Android)]
+public class Issue8680 : TestNavigationPage
+{
+ protected override void Init()
+ {
+ PushAsync(new Issue8680MainPage());
+ }
+}
+
+public class Issue8680MainPage : ContentPage
+{
+ public Issue8680MainPage()
+ {
+ var navigateButton = new Button
+ {
+ Text = "Go to Intercept Page",
+ AutomationId = "NavigateButton",
+ };
+ navigateButton.Clicked += async (s, e) =>
+ {
+ await Navigation.PushAsync(new Issue8680InterceptPage());
+ };
+
+ Content = new VerticalStackLayout
+ {
+ Children =
+ {
+ new Label { Text = "Main Page", AutomationId = "MainPageLabel" },
+ navigateButton,
+ }
+ };
+ }
+}
+
+public class Issue8680InterceptPage : ContentPage
+{
+ int _backPressCount;
+ readonly Label _statusLabel;
+
+ public Issue8680InterceptPage()
+ {
+ _statusLabel = new Label
+ {
+ Text = "Back not pressed yet",
+ AutomationId = "StatusLabel",
+ };
+
+ Content = new VerticalStackLayout
+ {
+ Children =
+ {
+ _statusLabel,
+ new Label { Text = "Press device back button — it should be intercepted", AutomationId = "InterceptPageLabel" },
+ }
+ };
+ }
+
+ protected override bool OnBackButtonPressed()
+ {
+ _backPressCount++;
+ _statusLabel.Text = $"Back intercepted: {_backPressCount}";
+ return true; // true = handled, prevents navigation back
+ }
+}
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/XFIssue/ShellFlyoutContent.cs b/src/Controls/tests/TestCases.HostApp/Issues/XFIssue/ShellFlyoutContent.cs
index a42484143ed9..a355f50b8bd7 100644
--- a/src/Controls/tests/TestCases.HostApp/Issues/XFIssue/ShellFlyoutContent.cs
+++ b/src/Controls/tests/TestCases.HostApp/Issues/XFIssue/ShellFlyoutContent.cs
@@ -2,8 +2,13 @@ namespace Maui.Controls.Sample.Issues;
[Issue(IssueTracker.None, 0, "Shell Flyout Content",
PlatformAffected.All)]
+public class ShellFlyoutContent : ShellFlyoutContentBase
+{
+}
-public class ShellFlyoutContent : TestShell
+// Base class with shared Init logic. No [Issue] attribute here so subclasses
+// can each declare their own without triggering AmbiguousMatchException.
+public abstract class ShellFlyoutContentBase : TestShell
{
protected override void Init()
{
diff --git a/src/Controls/tests/TestCases.HostApp/MauiProgram.cs b/src/Controls/tests/TestCases.HostApp/MauiProgram.cs
index beae20e666d9..df6a43e25b79 100644
--- a/src/Controls/tests/TestCases.HostApp/MauiProgram.cs
+++ b/src/Controls/tests/TestCases.HostApp/MauiProgram.cs
@@ -41,7 +41,8 @@ public static MauiApp CreateMauiApp()
.Issue18720DatePickerAddMappers()
.Issue18720TimePickerAddMappers()
.Issue28945AddMappers()
- .Issue25436RegisterNavigationService();
+ .Issue25436RegisterNavigationService()
+ .Issue36853RegisterServices();
#if IOS || MACCATALYST
appBuilder.ConfigureCollectionViewHandlers();
@@ -67,6 +68,9 @@ public static MauiApp CreateMauiApp()
#endif
#if IOS || MACCATALYST || ANDROID || WINDOWS
handlers.AddHandler(typeof(Issue34310NativeHostView), typeof(Issue34310NativeHostViewHandler));
+#endif
+#if ANDROID
+ handlers.AddHandler(typeof(Issue7814TouchClaimView), typeof(Issue7814TouchClaimViewHandler));
#endif
});
diff --git a/src/Controls/tests/TestCases.HostApp/Resources/Images/cancel_red.svg b/src/Controls/tests/TestCases.HostApp/Resources/Images/cancel_red.svg
deleted file mode 100644
index 75c1ae0a0021..000000000000
--- a/src/Controls/tests/TestCases.HostApp/Resources/Images/cancel_red.svg
+++ /dev/null
@@ -1,3 +0,0 @@
-
-
-
diff --git a/src/Controls/tests/TestCases.HostApp/Resources/Raw/HybridWebView1/image.html b/src/Controls/tests/TestCases.HostApp/Resources/Raw/HybridWebView1/image.html
index 3e8d01158f3e..df65e1b45317 100644
--- a/src/Controls/tests/TestCases.HostApp/Resources/Raw/HybridWebView1/image.html
+++ b/src/Controls/tests/TestCases.HostApp/Resources/Raw/HybridWebView1/image.html
@@ -206,8 +206,6 @@ 🌙 HybridWebView Test - Dark Theme
window.chrome.webview.postMessage("__RawMessage|" + msg);
} else if (window.webkit && window.webkit.messageHandlers && window.webkit.messageHandlers.webwindowinterop) {
window.webkit.messageHandlers.webwindowinterop.postMessage("__RawMessage|" + msg);
- } else if (window.hybridWebViewHost) {
- window.hybridWebViewHost.sendMessage("__RawMessage|" + msg);
}
}
};
diff --git a/src/Controls/tests/TestCases.HostApp/Resources/Raw/HybridWebView1/index.html b/src/Controls/tests/TestCases.HostApp/Resources/Raw/HybridWebView1/index.html
index abe2d1eff0f4..3be8594ba4c6 100644
--- a/src/Controls/tests/TestCases.HostApp/Resources/Raw/HybridWebView1/index.html
+++ b/src/Controls/tests/TestCases.HostApp/Resources/Raw/HybridWebView1/index.html
@@ -183,8 +183,6 @@ Usage Example
window.chrome.webview.postMessage("__RawMessage|" + msg);
} else if (window.webkit && window.webkit.messageHandlers && window.webkit.messageHandlers.webwindowinterop) {
window.webkit.messageHandlers.webwindowinterop.postMessage("__RawMessage|" + msg);
- } else if (window.hybridWebViewHost) {
- window.hybridWebViewHost.sendMessage("__RawMessage|" + msg);
}
}
};
diff --git a/src/Controls/tests/TestCases.HostApp/Resources/Raw/HybridWebView1/navigation.html b/src/Controls/tests/TestCases.HostApp/Resources/Raw/HybridWebView1/navigation.html
index 3869a1fb14da..e2bf9f43f2cf 100644
--- a/src/Controls/tests/TestCases.HostApp/Resources/Raw/HybridWebView1/navigation.html
+++ b/src/Controls/tests/TestCases.HostApp/Resources/Raw/HybridWebView1/navigation.html
@@ -193,9 +193,6 @@ ❓ Help
} else if (window.webkit && window.webkit.messageHandlers && window.webkit.messageHandlers.webwindowinterop) {
window.webkit.messageHandlers.webwindowinterop.postMessage("__RawMessage|" + msg);
return true;
- } else if (window.hybridWebViewHost) {
- window.hybridWebViewHost.sendMessage("__RawMessage|" + msg);
- return true;
}
return false;
} catch (error) {
diff --git a/src/Controls/tests/TestCases.HostApp/Resources/Raw/HybridWebView1/web.html b/src/Controls/tests/TestCases.HostApp/Resources/Raw/HybridWebView1/web.html
index 082087211e11..7be44ea21a02 100644
--- a/src/Controls/tests/TestCases.HostApp/Resources/Raw/HybridWebView1/web.html
+++ b/src/Controls/tests/TestCases.HostApp/Resources/Raw/HybridWebView1/web.html
@@ -94,8 +94,6 @@ Send Message
window.chrome.webview.postMessage("__RawMessage|" + msg);
} else if (window.webkit && window.webkit.messageHandlers && window.webkit.messageHandlers.webwindowinterop) {
window.webkit.messageHandlers.webwindowinterop.postMessage("__RawMessage|" + msg);
- } else if (window.hybridWebViewHost) {
- window.hybridWebViewHost.sendMessage("__RawMessage|" + msg);
}
}
};
diff --git a/src/Controls/tests/TestCases.HostApp/Resources/Raw/HybridWebView2/index.html b/src/Controls/tests/TestCases.HostApp/Resources/Raw/HybridWebView2/index.html
index 243d4707a9d0..b06eb08048f4 100644
--- a/src/Controls/tests/TestCases.HostApp/Resources/Raw/HybridWebView2/index.html
+++ b/src/Controls/tests/TestCases.HostApp/Resources/Raw/HybridWebView2/index.html
@@ -105,9 +105,6 @@ HybridWebView SendMessage and Receive Page
} else if (window.webkit && window.webkit.messageHandlers && window.webkit.messageHandlers.webwindowinterop) {
window.webkit.messageHandlers.webwindowinterop.postMessage("__RawMessage|" + msg);
return true;
- } else if (window.hybridWebViewHost) {
- window.hybridWebViewHost.sendMessage("__RawMessage|" + msg);
- return true;
}
return false;
} catch (e) {
diff --git a/src/Controls/tests/TestCases.HostApp/Resources/Raw/foo/bar/baz/test.html b/src/Controls/tests/TestCases.HostApp/Resources/Raw/foo/bar/baz/test.html
new file mode 100644
index 000000000000..a9a7f159cfbb
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Resources/Raw/foo/bar/baz/test.html
@@ -0,0 +1,10 @@
+
+
+
+ Nested Subdirectory Test File
+
+
+ Nested Subdirectory Test File
+ This is test.html from the foo/bar/baz nested subdirectories.
+
+
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/ClearPlaceholderIconShouldHideWhenDisabled.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/ClearPlaceholderIconShouldHideWhenDisabled.png
new file mode 100644
index 000000000000..ba8d777cd68b
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/ClearPlaceholderIconShouldHideWhenDisabled.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/FlyoutSelectedStateReflectsUpdatedDynamicResource.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/FlyoutSelectedStateReflectsUpdatedDynamicResource.png
new file mode 100644
index 000000000000..9b7f058c0571
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/FlyoutSelectedStateReflectsUpdatedDynamicResource.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/IndicatorViewCircleShape.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/IndicatorViewCircleShape.png
new file mode 100644
index 000000000000..e425e85ff245
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/IndicatorViewCircleShape.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/IndicatorViewSquareShape.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/IndicatorViewSquareShape.png
new file mode 100644
index 000000000000..2ff47841dd65
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/IndicatorViewSquareShape.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/ItemImageSourceShouldBeVisible.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/ItemImageSourceShouldBeVisible.png
index 023f757c1bc5..0835f6ee4df7 100644
Binary files a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/ItemImageSourceShouldBeVisible.png and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/ItemImageSourceShouldBeVisible.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/NavigationPageChildContentExtendsUnderFloatingTabBar.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/NavigationPageChildContentExtendsUnderFloatingTabBar.png
new file mode 100644
index 000000000000..0b8424ca0a6b
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/NavigationPageChildContentExtendsUnderFloatingTabBar.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/RadioButton_Checking_Initial_Configuration_VerifyVisualState.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/RadioButton_Checking_Initial_Configuration_VerifyVisualState.png
deleted file mode 100644
index 348791cc6f4a..000000000000
Binary files a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/RadioButton_Checking_Initial_Configuration_VerifyVisualState.png and /dev/null differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/RadioButton_SetFontAutoScalingEnabled_VerifyVisualState.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/RadioButton_SetFontAutoScalingEnabled_VerifyVisualState.png
new file mode 100644
index 000000000000..f5e5c4688e8e
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/RadioButton_SetFontAutoScalingEnabled_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/SearchBarClearButtonShouldBeVisibleWithText.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/SearchBarClearButtonShouldBeVisibleWithText.png
new file mode 100644
index 000000000000..53e2df90416f
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/SearchBarClearButtonShouldBeVisibleWithText.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/SearchBarClearButtonShouldDisappearAfterClearingInput.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/SearchBarClearButtonShouldDisappearAfterClearingInput.png
new file mode 100644
index 000000000000..941c51781046
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/SearchBarClearButtonShouldDisappearAfterClearingInput.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/SearchHandlerClearIconUpdatesAtRuntime.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/SearchHandlerClearIconUpdatesAtRuntime.png
new file mode 100644
index 000000000000..bfd66e452ade
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/SearchHandlerClearIconUpdatesAtRuntime.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/SearchHandlerClearPlaceholderIconUpdatesAtRuntime.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/SearchHandlerClearPlaceholderIconUpdatesAtRuntime.png
new file mode 100644
index 000000000000..e29ba4911fec
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/SearchHandlerClearPlaceholderIconUpdatesAtRuntime.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/SearchHandlerQueryIconUpdatesAtRuntime.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/SearchHandlerQueryIconUpdatesAtRuntime.png
new file mode 100644
index 000000000000..57781810fc19
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/SearchHandlerQueryIconUpdatesAtRuntime.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/SearchHandlerResetAllRestoresDefaultIcons.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/SearchHandlerResetAllRestoresDefaultIcons.png
new file mode 100644
index 000000000000..1685f8b19668
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/SearchHandlerResetAllRestoresDefaultIcons.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/SwipeItemFontAndSvgIconsRenderCorrectly.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/SwipeItemFontAndSvgIconsRenderCorrectly.png
deleted file mode 100644
index e53159d02778..000000000000
Binary files a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/SwipeItemFontAndSvgIconsRenderCorrectly.png and /dev/null differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/TabTitlesShouldNotBeTruncated.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/TabTitlesShouldNotBeTruncated.png
deleted file mode 100644
index bca93b457143..000000000000
Binary files a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/TabTitlesShouldNotBeTruncated.png and /dev/null differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/TappingDescendantInsideShadowedBorderShouldUpdateBackgroundColor.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/TappingDescendantInsideShadowedBorderShouldUpdateBackgroundColor.png
new file mode 100644
index 000000000000..8573155974ef
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/TappingDescendantInsideShadowedBorderShouldUpdateBackgroundColor.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyAnchorXAndAnchorYShadow.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyAnchorXAndAnchorYShadow.png
new file mode 100644
index 000000000000..2da157bbab89
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyAnchorXAndAnchorYShadow.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyAnchorXAndShadow.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyAnchorXAndShadow.png
new file mode 100644
index 000000000000..b2e0d9d4fe47
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyAnchorXAndShadow.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyAnchorYAndShadow.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyAnchorYAndShadow.png
new file mode 100644
index 000000000000..e196a3899cb7
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyAnchorYAndShadow.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyBorderWithNullStrokeDashArray.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyBorderWithNullStrokeDashArray.png
new file mode 100644
index 000000000000..0818f7537112
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyBorderWithNullStrokeDashArray.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyBorderWithStrokeDashArrayValue.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyBorderWithStrokeDashArrayValue.png
new file mode 100644
index 000000000000..5fa307a896fd
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyBorderWithStrokeDashArrayValue.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyCarouselScrollsToEndItemAfterReset.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyCarouselScrollsToEndItemAfterReset.png
new file mode 100644
index 000000000000..4c7d6a74d0bf
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyCarouselScrollsToEndItemAfterReset.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyCarouselViewKeepScrollOffsetAdd.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyCarouselViewKeepScrollOffsetAdd.png
new file mode 100644
index 000000000000..4e8bf912289d
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyCarouselViewKeepScrollOffsetAdd.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyCollectionViewContentWithButtonSwipeItem.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyCollectionViewContentWithButtonSwipeItem.png
index 3a193bfa5a5b..ce0aba34ecff 100644
Binary files a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyCollectionViewContentWithButtonSwipeItem.png and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyCollectionViewContentWithButtonSwipeItem.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyDefaultScrollToRequested.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyDefaultScrollToRequested.png
deleted file mode 100644
index 72698b9f46af..000000000000
Binary files a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyDefaultScrollToRequested.png and /dev/null differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyEditorBackgroundColorWithPlaceholder.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyEditorBackgroundColorWithPlaceholder.png
new file mode 100644
index 000000000000..5b5b93fe510a
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyEditorBackgroundColorWithPlaceholder.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyEditorBackgroundColorWithTextColor.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyEditorBackgroundColorWithTextColor.png
new file mode 100644
index 000000000000..faa0ed30cdc8
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyEditorBackgroundColorWithTextColor.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyEditorControlWhenPlaceholderColorSetDefaultValue.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyEditorControlWhenPlaceholderColorSetDefaultValue.png
new file mode 100644
index 000000000000..1176ac60479e
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyEditorControlWhenPlaceholderColorSetDefaultValue.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyEditorControlWhenPlaceholderTextSet.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyEditorControlWhenPlaceholderTextSet.png
index 3845fca234d9..a54bd61cee3c 100644
Binary files a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyEditorControlWhenPlaceholderTextSet.png and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyEditorControlWhenPlaceholderTextSet.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyEditorPlaceholderTextWhenFontAttributesBoldAndItalicSet.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyEditorPlaceholderTextWhenFontAttributesBoldAndItalicSet.png
new file mode 100644
index 000000000000..3689520548e6
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyEditorPlaceholderTextWhenFontAttributesBoldAndItalicSet.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyEditorPlaceholderWhenFlowDirectionSet.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyEditorPlaceholderWhenFlowDirectionSet.png
index 53dce3ca7d69..373903100949 100644
Binary files a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyEditorPlaceholderWhenFlowDirectionSet.png and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyEditorPlaceholderWhenFlowDirectionSet.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyEditorPlaceholderWithCharacterSpacing.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyEditorPlaceholderWithCharacterSpacing.png
index aed2fad3b742..5a9d5b7dc18c 100644
Binary files a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyEditorPlaceholderWithCharacterSpacing.png and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyEditorPlaceholderWithCharacterSpacing.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyEditorPlaceholderWithFontAttributes.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyEditorPlaceholderWithFontAttributes.png
index 8bc7128e75c3..a6c3f95fee58 100644
Binary files a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyEditorPlaceholderWithFontAttributes.png and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyEditorPlaceholderWithFontAttributes.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyEditorPlaceholderWithFontFamily.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyEditorPlaceholderWithFontFamily.png
index 46b914ef25c6..e662f69c79a6 100644
Binary files a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyEditorPlaceholderWithFontFamily.png and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyEditorPlaceholderWithFontFamily.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyEditorPlaceholderWithFontSize.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyEditorPlaceholderWithFontSize.png
index ae511d5f1a33..c73cdd9264ed 100644
Binary files a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyEditorPlaceholderWithFontSize.png and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyEditorPlaceholderWithFontSize.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyEditorPlaceholderWithHorizontalAlignment.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyEditorPlaceholderWithHorizontalAlignment.png
index 58cf1ccedb56..ed41e1df4ea7 100644
Binary files a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyEditorPlaceholderWithHorizontalAlignment.png and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyEditorPlaceholderWithHorizontalAlignment.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyEditorPlaceholderWithShadow.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyEditorPlaceholderWithShadow.png
index 44d6814a011a..1adcb301cdd9 100644
Binary files a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyEditorPlaceholderWithShadow.png and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyEditorPlaceholderWithShadow.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyEditorPlaceholderWithVerticalAlignment.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyEditorPlaceholderWithVerticalAlignment.png
index 59070f314737..ddb1ef092940 100644
Binary files a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyEditorPlaceholderWithVerticalAlignment.png and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyEditorPlaceholderWithVerticalAlignment.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyEditorTextColorSetDefaultValue.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyEditorTextColorSetDefaultValue.png
new file mode 100644
index 000000000000..4e6dec975109
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyEditorTextColorSetDefaultValue.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyEditorTextWhenAlignedHorizontally.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyEditorTextWhenAlignedHorizontally.png
new file mode 100644
index 000000000000..bc421ef4b43f
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyEditorTextWhenAlignedHorizontally.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyEditorTextWhenAlignedVertically.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyEditorTextWhenAlignedVertically.png
new file mode 100644
index 000000000000..a9fa55f11fe0
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyEditorTextWhenAlignedVertically.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyEditorTextWhenAlingnedHorizontally.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyEditorTextWhenAlingnedHorizontally.png
deleted file mode 100644
index 23acf9357054..000000000000
Binary files a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyEditorTextWhenAlingnedHorizontally.png and /dev/null differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyEditorTextWhenAlingnedVertically.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyEditorTextWhenAlingnedVertically.png
deleted file mode 100644
index fee75d4081ea..000000000000
Binary files a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyEditorTextWhenAlingnedVertically.png and /dev/null differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyEditorTextWhenAutoSizeDisabled.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyEditorTextWhenAutoSizeDisabled.png
new file mode 100644
index 000000000000..ce2e9ca8bb3e
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyEditorTextWhenAutoSizeDisabled.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyEditorTextWhenAutoSizeTextChangesSet.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyEditorTextWhenAutoSizeTextChangesSet.png
new file mode 100644
index 000000000000..863b70ecab7e
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyEditorTextWhenAutoSizeTextChangesSet.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyEditorTextWhenAutoSizeTextChangesSetWithHeightRequest.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyEditorTextWhenAutoSizeTextChangesSetWithHeightRequest.png
new file mode 100644
index 000000000000..c4d25773693d
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyEditorTextWhenAutoSizeTextChangesSetWithHeightRequest.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyEditorTextWhenAutoSizeTextChangesSetWithShortShrinkText_LongText.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyEditorTextWhenAutoSizeTextChangesSetWithShortShrinkText_LongText.png
new file mode 100644
index 000000000000..93728351f9db
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyEditorTextWhenAutoSizeTextChangesSetWithShortShrinkText_LongText.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyEditorTextWhenAutoSizeTextChangesSetWithShortShrinkText_ShortText.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyEditorTextWhenAutoSizeTextChangesSetWithShortShrinkText_ShortText.png
new file mode 100644
index 000000000000..3ada5be66e77
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyEditorTextWhenAutoSizeTextChangesSetWithShortShrinkText_ShortText.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyEditorTextWhenFontAttributesBoldAndItalicSet.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyEditorTextWhenFontAttributesBoldAndItalicSet.png
new file mode 100644
index 000000000000..3ecb2855643f
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyEditorTextWhenFontAttributesBoldAndItalicSet.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyEditorTextWhenFontFamilySetValue.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyEditorTextWhenFontFamilySetValue.png
index c4d97d643006..a68f8cd1c3e9 100644
Binary files a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyEditorTextWhenFontFamilySetValue.png and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyEditorTextWhenFontFamilySetValue.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyEditorWhenBackgroundColorSet.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyEditorWhenBackgroundColorSet.png
new file mode 100644
index 000000000000..06fff91fc2d6
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyEditorWhenBackgroundColorSet.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyEditorWhenHeightAndWidthRequestSet.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyEditorWhenHeightAndWidthRequestSet.png
new file mode 100644
index 000000000000..9691c285eb6d
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyEditorWhenHeightAndWidthRequestSet.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyEditorWhenHeightRequestSet.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyEditorWhenHeightRequestSet.png
new file mode 100644
index 000000000000..2dab294e1f5a
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyEditorWhenHeightRequestSet.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyEditorWhenOpacityResetToDefault.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyEditorWhenOpacityResetToDefault.png
new file mode 100644
index 000000000000..fb423163cc4f
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyEditorWhenOpacityResetToDefault.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyEditorWhenOpacitySet.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyEditorWhenOpacitySet.png
new file mode 100644
index 000000000000..23ae98fd926b
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyEditorWhenOpacitySet.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyEditorWhenOpacitySetToZero.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyEditorWhenOpacitySetToZero.png
new file mode 100644
index 000000000000..63ec27c8dd95
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyEditorWhenOpacitySetToZero.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyEditorWhenWidthRequestSet.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyEditorWhenWidthRequestSet.png
new file mode 100644
index 000000000000..9aceba47bdfe
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyEditorWhenWidthRequestSet.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyEditorWithShadow.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyEditorWithShadow.png
new file mode 100644
index 000000000000..cc1a2751334a
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyEditorWithShadow.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyEditor_WithShadow.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyEditor_WithShadow.png
deleted file mode 100644
index ac43f8c7202c..000000000000
Binary files a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyEditor_WithShadow.png and /dev/null differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyLabelWithTextAndLineBreakModeHeadTruncation.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyLabelWithTextAndLineBreakModeHeadTruncation.png
index 2ce217d20b5c..e6035e3b71c4 100644
Binary files a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyLabelWithTextAndLineBreakModeHeadTruncation.png and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyLabelWithTextAndLineBreakModeHeadTruncation.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyLabelWithTextAndLineBreakModeMiddleTruncation.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyLabelWithTextAndLineBreakModeMiddleTruncation.png
index 2ecec2c8fa1d..f7853a24b5b1 100644
Binary files a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyLabelWithTextAndLineBreakModeMiddleTruncation.png and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyLabelWithTextAndLineBreakModeMiddleTruncation.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyLabelWithTextWhenLineHeight.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyLabelWithTextWhenLineHeight.png
index 9504ad180913..badaf08d97b2 100644
Binary files a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyLabelWithTextWhenLineHeight.png and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyLabelWithTextWhenLineHeight.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyRotationAndShadow.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyRotationAndShadow.png
new file mode 100644
index 000000000000..c8672e88d732
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyRotationAndShadow.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyRotationXAndShadow.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyRotationXAndShadow.png
new file mode 100644
index 000000000000..751d5ca37685
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyRotationXAndShadow.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyRotationYAndShadow.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyRotationYAndShadow.png
new file mode 100644
index 000000000000..cdbb90bcf899
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyRotationYAndShadow.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyScaleAndShadow.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyScaleAndShadow.png
new file mode 100644
index 000000000000..a9e47a8f14fb
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyScaleAndShadow.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyScaleXAndShadow.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyScaleXAndShadow.png
new file mode 100644
index 000000000000..6f90f6e55d4e
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyScaleXAndShadow.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyScaleYAndShadow.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyScaleYAndShadow.png
new file mode 100644
index 000000000000..b5ad25783407
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyScaleYAndShadow.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyScrollViewDirection.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyScrollViewDirection.png
new file mode 100644
index 000000000000..4c09241af14a
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyScrollViewDirection.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyScrollViewHeightWhenRemoveChildAtRuntime.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyScrollViewHeightWhenRemoveChildAtRuntime.png
new file mode 100644
index 000000000000..4e5f5691dad0
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyScrollViewHeightWhenRemoveChildAtRuntime.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifySwipeViewApperance.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifySwipeViewApperance.png
index 620dced29d24..a25b5a1cfbbe 100644
Binary files a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifySwipeViewApperance.png and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifySwipeViewApperance.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifySwipeViewWithButtonSwipeItemsBackgroundColor.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifySwipeViewWithButtonSwipeItemsBackgroundColor.png
index 46f3858091ac..919d2315e6a5 100644
Binary files a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifySwipeViewWithButtonSwipeItemsBackgroundColor.png and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifySwipeViewWithButtonSwipeItemsBackgroundColor.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifySwipeViewWithCollectionViewContentAndThreshold.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifySwipeViewWithCollectionViewContentAndThreshold.png
index 6771f92cdb4f..bdc5f2c6d937 100644
Binary files a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifySwipeViewWithCollectionViewContentAndThreshold.png and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifySwipeViewWithCollectionViewContentAndThreshold.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifySwipeViewWithImageContentAndThreshold.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifySwipeViewWithImageContentAndThreshold.png
index 09093ae3ae0a..8a63b9a2a209 100644
Binary files a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifySwipeViewWithImageContentAndThreshold.png and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifySwipeViewWithImageContentAndThreshold.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifySwipeViewWithLabelContentAndThreshold.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifySwipeViewWithLabelContentAndThreshold.png
index f428914ca39c..c23c5beca0d1 100644
Binary files a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifySwipeViewWithLabelContentAndThreshold.png and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifySwipeViewWithLabelContentAndThreshold.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyTitleBarContentinFullScreenmode.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyTitleBarContentinFullScreenmode.png
new file mode 100644
index 000000000000..c0b917d82b1b
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyTitleBarContentinFullScreenmode.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyTranslationXAndShadow.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyTranslationXAndShadow.png
new file mode 100644
index 000000000000..de9d335b7873
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyTranslationXAndShadow.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyTranslationYAndShadow.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyTranslationYAndShadow.png
new file mode 100644
index 000000000000..ebea2b9c1558
Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyTranslationYAndShadow.png differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyzEditorTextWhenAutoSizeDisabled.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyzEditorTextWhenAutoSizeDisabled.png
deleted file mode 100644
index 830e126fc185..000000000000
Binary files a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyzEditorTextWhenAutoSizeDisabled.png and /dev/null differ
diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyzEditorTextWhenAutoSizeTextChangesSet.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyzEditorTextWhenAutoSizeTextChangesSet.png
deleted file mode 100644
index 12bc8dffa90a..000000000000
Binary files a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/VerifyzEditorTextWhenAutoSizeTextChangesSet.png and /dev/null differ
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/CollectionView_ScrollingFeatureTests.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/CollectionView_ScrollingFeatureTests.cs
index a7b1196499a1..509241f467a5 100644
--- a/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/CollectionView_ScrollingFeatureTests.cs
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/CollectionView_ScrollingFeatureTests.cs
@@ -1573,7 +1573,6 @@ public void VerifyDefaultScrollToRequested()
App.Tap("ScrollTo");
App.WaitForElement("ScrollToRequestedLabel");
Assert.That(App.WaitForElement("ScrollToRequestedLabel").GetText(), Is.EqualTo("Fired"));
- VerifyScreenshot();
}
// ScrollTo By Index Tests
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/EditorFeatureTests.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/EditorFeatureTests.cs
index 0bd290da343e..47c64cbd65c9 100644
--- a/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/EditorFeatureTests.cs
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/EditorFeatureTests.cs
@@ -25,33 +25,37 @@ public EditorFeatureTests(TestDevice device)
: base(device)
{
}
+ // Note: FontAutoScaling states cannot currently be reliably covered in CI environments, as system font scaling settings are not consistently supported or controllable in automated runs.
- [Test, Order(0)]
+#if TEST_FAILS_ON_WINDOWS //Related issue: https://github.com/dotnet/maui/issues/29805
+ [Test, Order(1)]
public void VerifyEditorInitialEventStates()
{
App.WaitForElement("TestEditor");
+ Assert.That(App.WaitForElement("FocusedLabel").GetText(), Is.EqualTo("Focused: Not triggered"));
Assert.That(App.WaitForElement("UnfocusedLabel").GetText(), Is.EqualTo("Unfocused: Not triggered"));
Assert.That(App.WaitForElement("CompletedLabel").GetText(), Is.EqualTo("Completed: Not triggered"));
Assert.That(App.WaitForElement("TextChangedLabel").GetText(), Is.EqualTo("TextChanged: Old='', New='Test Editor'"));
}
+#endif
- [Test, Order(4)]
- public void VerifyEditorCompletedEvent()
+ [Test, Order(2)]
+ public async Task VerifyEditorFocusedEvent()
{
App.WaitForElement("TestEditor");
App.Tap("TestEditor");
- App.PressEnter();
-#if ANDROID
- App.DismissKeyboard();
-#else
- App.WaitForElement("EditorControlTitleLabel");
- App.Tap("EditorControlTitleLabel");
+ await Task.Delay(100);
+#if ANDROID || IOS
+ if (App.IsKeyboardShown())
+ {
+ App.DismissKeyboard();
+ }
#endif
- Assert.That(App.WaitForElement("CompletedLabel").GetText(), Is.EqualTo("Completed: Event Triggered"));
+ Assert.That(App.WaitForElement("FocusedLabel").GetText(), Is.EqualTo("Focused: Event Triggered"));
}
#if TEST_FAILS_ON_CATALYST && TEST_FAILS_ON_IOS //when using App.EnterText() in a multiline field like an Editor, it types the text and then presses the Return key — which adds a new line.
- [Test, Order(2)]
+ [Test, Order(3)]
public void VerifyEditorTextChangedEvent()
{
@@ -67,39 +71,39 @@ public void VerifyEditorTextChangedEvent()
}
#endif
- [Test, Order(1)]
- public async Task VerifyEditorFocusedEvent()
+ [Test, Order(4)]
+ public async Task VerifyEditorUnfocusedEvent()
{
App.WaitForElement("TestEditor");
- App.Tap("TestEditor");
+ App.WaitForElement("SelectionLengthEntry");
+ App.Tap("SelectionLengthEntry");
await Task.Delay(100);
-#if ANDROID || IOS
- if (App.IsKeyboardShown())
- {
- App.DismissKeyboard();
- }
+#if ANDROID
+ App.DismissKeyboard();
+#else
+ App.WaitForElement("EditorControlTitleLabel");
+ App.Tap("EditorControlTitleLabel");
#endif
- Assert.That(App.WaitForElement("FocusedLabel").GetText(), Is.EqualTo("Focused: Event Triggered"));
+ Assert.That(App.WaitForElement("UnfocusedLabel").GetText(), Is.EqualTo("Unfocused: Event Triggered"));
}
- [Test, Order(3)]
- public async Task VerifyEditorUnfocusedEvent()
+ [Test, Order(5)]
+ public void VerifyEditorCompletedEvent()
{
App.WaitForElement("TestEditor");
- App.WaitForElement("SelectionLengthEntry");
- App.Tap("SelectionLengthEntry");
- await Task.Delay(100);
+ App.Tap("TestEditor");
+ App.PressEnter();
#if ANDROID
App.DismissKeyboard();
#else
App.WaitForElement("EditorControlTitleLabel");
App.Tap("EditorControlTitleLabel");
#endif
- Assert.That(App.WaitForElement("UnfocusedLabel").GetText(), Is.EqualTo("Unfocused: Event Triggered"));
+ Assert.That(App.WaitForElement("CompletedLabel").GetText(), Is.EqualTo("Completed: Event Triggered"));
}
- [Test]
- public void VerifyEditorTextWhenAlingnedHorizontally()
+ [Test, Order(6)]
+ public void VerifyEditorTextWhenAlignedHorizontally()
{
App.WaitForElement("Options");
App.Tap("Options");
@@ -111,8 +115,8 @@ public void VerifyEditorTextWhenAlingnedHorizontally()
VerifyScreenshot(cropBottom: CropBottomValue);
}
- [Test]
- public void VerifyEditorTextWhenAlingnedVertically()
+ [Test, Order(7)]
+ public void VerifyEditorTextWhenAlignedVertically()
{
App.Tap("Options");
App.WaitForElement("VEnd");
@@ -124,8 +128,8 @@ public void VerifyEditorTextWhenAlingnedVertically()
}
#if TEST_FAILS_ON_ANDROID // On Android, using App.EnterText in UI tests (e.g., with Appium UITest) can programmatically enter text into an Editor control even if its IsReadOnly property is set to true.
- [Test]
- public void VerifyTextEditorWhenSetAsReadOnly()
+ [Test, Order(8)]
+ public void VerifyEditorWhenIsReadOnlyTrue()
{
App.WaitForElement("Options");
App.Tap("Options");
@@ -139,7 +143,7 @@ public void VerifyTextEditorWhenSetAsReadOnly()
}
#endif
- [Test]
+ [Test, Order(9)]
public void VerifyEditorTextWhenFontFamilySetValue()
{
App.WaitForElement("Options");
@@ -152,7 +156,7 @@ public void VerifyEditorTextWhenFontFamilySetValue()
VerifyScreenshot(cropBottom: CropBottomValue);
}
- [Test]
+ [Test, Order(10)]
public void VerifyEditorTextWhenCharacterSpacingSetValues()
{
App.WaitForElement("Options");
@@ -166,7 +170,7 @@ public void VerifyEditorTextWhenCharacterSpacingSetValues()
VerifyScreenshot(cropBottom: CropBottomValue);
}
- [Test]
+ [Test, Order(11)]
public void VerifyEditorHorizontalTextAlignmentBasedOnCharacterSpacing()
{
App.WaitForElement("Options");
@@ -182,7 +186,7 @@ public void VerifyEditorHorizontalTextAlignmentBasedOnCharacterSpacing()
VerifyScreenshot(cropBottom: CropBottomValue);
}
- [Test]
+ [Test, Order(12)]
public void VerifyEditorVerticalTextAlignmentBasedOnCharacterSpacing()
{
App.WaitForElement("Options");
@@ -198,7 +202,7 @@ public void VerifyEditorVerticalTextAlignmentBasedOnCharacterSpacing()
VerifyScreenshot(cropBottom: CropBottomValue);
}
- [Test]
+ [Test, Order(13)]
public void VerifyEditorCharacterSpacingWhenFontFamily()
{
App.WaitForElement("Options");
@@ -214,7 +218,7 @@ public void VerifyEditorCharacterSpacingWhenFontFamily()
VerifyScreenshot(cropBottom: CropBottomValue);
}
- [Test]
+ [Test, Order(14)]
public void VerifyEditorCharacterSpacingWhenMaxLengthSet()
{
App.WaitForElement("Options");
@@ -224,7 +228,7 @@ public void VerifyEditorCharacterSpacingWhenMaxLengthSet()
App.EnterText("CharacterSpacing", "5");
App.WaitForElement("TextEntryChanged");
App.ClearText("TextEntryChanged");
- App.EnterText("TextEntryChanged", "Test Entered Set MaxLenght");
+ App.EnterText("TextEntryChanged", "Test Entered Set MaxLength");
App.WaitForElement("MaxLength");
App.ClearText("MaxLength");
App.EnterText("MaxLength", "6");
@@ -234,14 +238,14 @@ public void VerifyEditorCharacterSpacingWhenMaxLengthSet()
VerifyScreenshot(cropBottom: CropBottomValue);
}
- [Test]
+ [Test, Order(15)]
public void VerifyEditorTextWhenMaxLengthSetValue()
{
App.WaitForElement("Options");
App.Tap("Options");
App.WaitForElement("TextEntryChanged");
App.ClearText("TextEntryChanged");
- App.EnterText("TextEntryChanged", "Test Entered Set MaxLenght");
+ App.EnterText("TextEntryChanged", "Test Entered Set MaxLength");
App.WaitForElement("MaxLength");
App.ClearText("MaxLength");
App.EnterText("MaxLength", "6");
@@ -249,17 +253,20 @@ public void VerifyEditorTextWhenMaxLengthSetValue()
App.Tap("Apply");
App.WaitForElement("TestEditor");
Assert.That(App.WaitForElement("TestEditor").GetText(), Is.EqualTo("Test E"));
+ App.ClearText("TestEditor");
+ App.EnterText("TestEditor", "1234567890");
+ Assert.That(App.WaitForElement("TestEditor").GetText(), Is.EqualTo("123456"));
}
#if TEST_FAILS_ON_ANDROID // On Android, using App.EnterText in UI tests (e.g., with Appium UITest) can programmatically enter text into an Editor control even if its IsReadOnly property is set to true.
- [Test]
+ [Test, Order(16)]
public void VerifyEditorMaxLengthWhenIsReadOnlyTrue()
{
App.WaitForElement("Options");
App.Tap("Options");
App.WaitForElement("TextEntryChanged");
App.ClearText("TextEntryChanged");
- App.EnterText("TextEntryChanged", "Test Entered Set MaxLenght");
+ App.EnterText("TextEntryChanged", "Test Entered Set MaxLength");
App.WaitForElement("ReadOnlyTrue");
App.Tap("ReadOnlyTrue");
App.WaitForElement("MaxLength");
@@ -270,11 +277,10 @@ public void VerifyEditorMaxLengthWhenIsReadOnlyTrue()
App.WaitForElement("TestEditor");
App.EnterText("TestEditor", "123");
Assert.That(App.WaitForElement("TestEditor").GetText(), Is.EqualTo("Test E"));
-
}
#endif
- [Test]
+ [Test, Order(17)]
public void VerifyEditorHorizontalTextAlignmentWhenVerticalTextAlignmentSet()
{
App.WaitForElement("Options");
@@ -289,7 +295,7 @@ public void VerifyEditorHorizontalTextAlignmentWhenVerticalTextAlignmentSet()
VerifyScreenshot(cropBottom: CropBottomValue);
}
- [Test]
+ [Test, Order(18)]
public void VerifyEditorTextWhenTextColorSetCorrectly()
{
App.WaitForElement("Options");
@@ -299,11 +305,33 @@ public void VerifyEditorTextWhenTextColorSetCorrectly()
App.WaitForElement("Apply");
App.Tap("Apply");
App.WaitForElement("TestEditor");
- App.Tap("Editor Control"); // Add an additional tap to make the Editor control unfocus.
+ App.Tap("EditorControlTitleLabel"); // Add an additional tap to make the Editor control unfocus.
+ VerifyScreenshot(cropBottom: CropBottomValue);
+ }
+
+ [Test, Order(19)]
+ public void VerifyEditorTextColorSetDefaultValue()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("TextColorBlue");
+ App.Tap("TextColorBlue");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElement("TestEditor");
+ App.Tap("EditorControlTitleLabel"); // Add an additional tap to make the Editor control unfocus.
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("TextColorDefault");
+ App.Tap("TextColorDefault");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElement("TestEditor");
+ App.Tap("EditorControlTitleLabel"); // Add an additional tap to make the Editor control unfocus.
VerifyScreenshot(cropBottom: CropBottomValue);
}
- [Test]
+ [Test, Order(20)]
public void VerifyEditorTextWhenFontSizeSetCorrectly()
{
App.WaitForElement("Options");
@@ -317,42 +345,42 @@ public void VerifyEditorTextWhenFontSizeSetCorrectly()
VerifyScreenshot(cropBottom: CropBottomValue);
}
-#if TEST_FAILS_ON_ANDROID && TEST_FAILS_ON_CATALYST && TEST_FAILS_ON_IOS && TEST_FAILS_ON_WINDOWS //related issue link: https://github.com/dotnet/maui/issues/29833
- [Test]
- public void VerifyEditorTextWhenIsTextPredictionEnabledTrueOrFalse()
- {
- App.WaitForElement("Options");
- App.Tap("Options");
- App.WaitForElement("TextPredictionTrue");
- App.Tap("TextPredictionTrue");
- App.WaitForElement("Apply");
- App.Tap("Apply");
- App.WaitForElement("TestEditor");
- App.ClearText("TestEditor");
- App.EnterText("TestEditor", "Testig");
- App.EnterText("TestEditor", " ");
- Assert.That(App.WaitForElement("TestEditor").GetText(), Is.EqualTo("Testing "));
- }
+ [Test, Order(21)]
+ [Ignore("Fails on all platforms, related issue link: https://github.com/dotnet/maui/issues/29833")]
+ public void VerifyEditorTextWhenIsTextPredictionEnabledTrue()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("TextPredictionTrue");
+ App.Tap("TextPredictionTrue");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElement("TestEditor");
+ App.ClearText("TestEditor");
+ App.EnterText("TestEditor", "Testig");
+ App.EnterText("TestEditor", " ");
+ Assert.That(App.WaitForElement("TestEditor").GetText(), Is.EqualTo("Testing "));
+ }
- [Test]
- public void VerifyEditorTextWhenIsSpellCheckEnabledTrueOrFalse()
- {
- App.WaitForElement("Options");
- App.Tap("Options");
- App.WaitForElement("SpellCheckTrue");
- App.Tap("SpellCheckTrue");
- App.WaitForElement("Apply");
- App.Tap("Apply");
- App.WaitForElement("TestEditor");
- App.ClearText("TestEditor");
- App.EnterText("TestEditor", "Testig");
- App.EnterText("TestEditor", " ");
- VerifyScreenshotWithKeyboardHandling();
- }
-#endif
+ [Test, Order(22)]
+ [Ignore("Fails on all platforms, related issue link: https://github.com/dotnet/maui/issues/29833")]
+ public void VerifyEditorTextWhenIsSpellCheckEnabledTrue()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("SpellCheckTrue");
+ App.Tap("SpellCheckTrue");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElement("TestEditor");
+ App.ClearText("TestEditor");
+ App.EnterText("TestEditor", "Testig");
+ App.EnterText("TestEditor", " ");
+ VerifyScreenshotWithKeyboardHandling();
+ }
#if TEST_FAILS_ON_CATALYST && TEST_FAILS_ON_IOS && TEST_FAILS_ON_ANDROID // On iOS and Maccatalyst While updating CursorPosition and SelectionLength, the Editor text gets deleted. & On Android, changing CursorPosition keeps the cursor visible even when IsCursorVisible is set to false, which is unexpected.
- [Test, Order(5)]
+ [Test, Order(23)]
public void VerifyEditorTextWhenSelectionLengthSetValue()
{
App.WaitForElement("Options");
@@ -371,7 +399,7 @@ public void VerifyEditorTextWhenSelectionLengthSetValue()
Assert.That(App.WaitForElement("SelectionLengthEntry").GetText(), Is.EqualTo("0"));
}
- [Test]
+ [Test, Order(24)]
public void VerifyEditorTextWhenCursorPositionValueSet()
{
App.WaitForElement("Options");
@@ -384,13 +412,14 @@ public void VerifyEditorTextWhenCursorPositionValueSet()
App.DismissKeyboard();
App.WaitForElement("UpdateCursorAndSelectionButton");
App.Tap("UpdateCursorAndSelectionButton");
+ Assert.That(App.WaitForElement("CursorPositionEntry").GetText(), Is.EqualTo("5"));
App.WaitForElement("TestEditor");
App.Tap("TestEditor");
App.DismissKeyboard();
Assert.That(App.WaitForElement("CursorPositionEntry").GetText(), Is.EqualTo("11"));
}
- [Test]
+ [Test, Order(25)]
public void VerifyEditorCursorPositionWhenSelectionLengthSetValue()
{
App.WaitForElement("Options");
@@ -406,6 +435,8 @@ public void VerifyEditorCursorPositionWhenSelectionLengthSetValue()
App.DismissKeyboard();
App.WaitForElement("UpdateCursorAndSelectionButton");
App.Tap("UpdateCursorAndSelectionButton");
+ Assert.That(App.WaitForElement("CursorPositionEntry").GetText(), Is.EqualTo("3"));
+ Assert.That(App.WaitForElement("SelectionLengthEntry").GetText(), Is.EqualTo("5"));
App.WaitForElement("TestEditor");
App.Tap("TestEditor");
App.DismissKeyboard();
@@ -415,7 +446,7 @@ public void VerifyEditorCursorPositionWhenSelectionLengthSetValue()
#endif
#if TEST_FAILS_ON_WINDOWS // On Windows, cursor position and selection length still work when the Entry is set to read-only.
- [Test]
+ [Test, Order(26)]
public void VerifyEditorCursorPositionWhenIsReadOnlyTrue()
{
App.WaitForElement("Options");
@@ -429,8 +460,8 @@ public void VerifyEditorCursorPositionWhenIsReadOnlyTrue()
Assert.That(App.WaitForElement("CursorPositionEntry").GetText(), Is.EqualTo("0"));
}
- [Test]
- public void VerifyEditorSelectionLenghtWhenIsReadOnlyTrue()
+ [Test, Order(27)]
+ public void VerifyEditorSelectionLengthWhenIsReadOnlyTrue()
{
App.WaitForElement("Options");
App.Tap("Options");
@@ -449,8 +480,8 @@ public void VerifyEditorSelectionLenghtWhenIsReadOnlyTrue()
}
#endif
-#if TEST_FAILS_ON_CATALYST && TEST_FAILS_ON_WINDOWS && TEST_FAILS_ON_ANDROID && TEST_FAILS_ON_IOS //keybord type is not supported on Windows and Maccatalyst platforms & On Android & IOS related issue:https://github.com/dotnet/maui/issues/26968
- [Test]
+ [Test, Order(28)]
+ [Ignore("Fails on all platforms, the keybord type is not supported on Windows and Maccatalyst platforms & On Android & IOS related issue:https://github.com/dotnet/maui/issues/26968")]
public void VerifyEditorTextWhenKeyboardTypeSet()
{
App.WaitForElement("Options");
@@ -464,25 +495,9 @@ public void VerifyEditorTextWhenKeyboardTypeSet()
VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
}
- [Test]
- public void VerifyEditorTextWhenReturnTypeSet()
- {
- App.WaitForElement("Options");
- App.Tap("Options");
- App.WaitForElement("Search");
- App.Tap("Search");
- App.WaitForElement("Apply");
- App.Tap("Apply");
- App.WaitForElement("TestEditor");
- App.Tap("TestEditor");
- VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
- }
-
-#endif
-
#if TEST_FAILS_ON_ANDROID // On Android, using App.EnterText in UI tests (e.g., with Appium UITest) can programmatically enter text into an Entry control even if its IsEnabled property is set to false.
- [Test]
- public void VerifyEditorControlWhenIsEnabledTrueOrFalse()
+ [Test, Order(29)]
+ public void VerifyEditorControlWhenIsEnabledFalse()
{
App.WaitForElement("Options");
App.Tap("Options");
@@ -496,14 +511,9 @@ public void VerifyEditorControlWhenIsEnabledTrueOrFalse()
}
#endif
- [Test]
- public void VerifyEditorControlWhenIsVisibleTrueOrFalse()
+ [Test, Order(30)]
+ public void VerifyEditorControlWhenIsVisibleFalse()
{
- App.WaitForElement("Options");
- App.Tap("Options");
- App.WaitForElement("Apply");
- App.Tap("Apply");
- App.WaitForElement("TestEditor");
App.WaitForElement("Options");
App.Tap("Options");
App.WaitForElement("VisibleFalse");
@@ -513,7 +523,7 @@ public void VerifyEditorControlWhenIsVisibleTrueOrFalse()
App.WaitForNoElement("TestEditor");
}
- [Test]
+ [Test, Order(31)]
public void VerifyEditorControlWhenFlowDirectionSet()
{
App.WaitForElement("Options");
@@ -527,7 +537,7 @@ public void VerifyEditorControlWhenFlowDirectionSet()
}
#if TEST_FAILS_ON_WINDOWS //On Windows, the placeholder is not visible because its text alignment is reset to default values when navigating to the page. This issue occurs only in the Host App.
- [Test]
+ [Test, Order(32)]
public void VerifyEditorPlaceholderWhenFlowDirectionSet()
{
App.WaitForElement("Options");
@@ -546,7 +556,7 @@ public void VerifyEditorPlaceholderWhenFlowDirectionSet()
}
- [Test]
+ [Test, Order(33)]
public void VerifyEditorControlWhenPlaceholderTextSet()
{
App.WaitForElement("Options");
@@ -561,7 +571,7 @@ public void VerifyEditorControlWhenPlaceholderTextSet()
VerifyScreenshot(cropBottom: CropBottomValue);
}
- [Test]
+ [Test, Order(34)]
public void VerifyEditorControlWhenPlaceholderColorSet()
{
App.WaitForElement("Options");
@@ -578,10 +588,35 @@ public void VerifyEditorControlWhenPlaceholderColorSet()
App.WaitForElement("TestEditor");
VerifyScreenshot(cropBottom: CropBottomValue);
}
+
+ [Test, Order(35)]
+ public void VerifyEditorControlWhenPlaceholderColorSetDefaultValue()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("PlaceholderColorRed");
+ App.Tap("PlaceholderColorRed");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElement("TestEditor");
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("PlaceholderColorDefault");
+ App.Tap("PlaceholderColorDefault");
+ App.WaitForElement("PlaceholderText");
+ App.ClearText("PlaceholderText");
+ App.EnterText("PlaceholderText", "Enter your name");
+ App.WaitForElement("TextEntryChanged");
+ App.ClearText("TextEntryChanged");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElement("TestEditor");
+ VerifyScreenshot(cropBottom: CropBottomValue);
+ }
#endif
- [Test]
- public void VerifyEditorWhenTextChanged()
+ [Test, Order(36)]
+ public void VerifyEditorTextDynamicChange()
{
App.WaitForElement("Options");
App.Tap("Options");
@@ -593,7 +628,7 @@ public void VerifyEditorWhenTextChanged()
Assert.That(App.WaitForElement("TestEditor").GetText(), Is.EqualTo("New Text Changed"));
}
- [Test]
+ [Test, Order(37)]
public void VerifyEditorTextWhenFontAttributesSet()
{
App.WaitForElement("Options");
@@ -606,8 +641,8 @@ public void VerifyEditorTextWhenFontAttributesSet()
VerifyScreenshot(cropBottom: CropBottomValue);
}
- [Test]
- public void VerifyEditorTextWhenTextTransFormSet()
+ [Test, Order(38)]
+ public void VerifyEditorTextWhenTextTransformUppercase()
{
App.WaitForElement("Options");
App.Tap("Options");
@@ -619,39 +654,22 @@ public void VerifyEditorTextWhenTextTransFormSet()
Assert.That(App.WaitForElement("TestEditor").GetText(), Is.EqualTo("TEST EDITOR"));
}
- [Test]
- public void VerifyzEditorTextWhenAutoSizeTextChangesSet()
+ [Test, Order(39)]
+ public void VerifyEditorTextWhenTextTransformLowercase()
{
App.WaitForElement("Options");
App.Tap("Options");
- App.WaitForElement("AutoSizeTextChanges");
- App.Tap("AutoSizeTextChanges");
- App.WaitForElement("TextEntryChanged");
- App.ClearText("TextEntryChanged");
- App.EnterText("TextEntryChanged", "When auto-resizing is enabled, the height of the Editor will increase when the user fills it with text, and the height will decrease as the user deletes text. This can be used to ensure that Editor objects in a DataTemplate.");
+ App.WaitForElement("TextTransformLowercase");
+ App.Tap("TextTransformLowercase");
App.WaitForElement("Apply");
App.Tap("Apply");
App.WaitForElement("TestEditor");
- VerifyScreenshotWithKeyboardHandling();
- }
-
- [Test]
- public void VerifyzEditorTextWhenAutoSizeDisabled()
- {
- App.WaitForElement("Options");
- App.Tap("Options");
- App.WaitForElement("TextEntryChanged");
- App.ClearText("TextEntryChanged");
- App.EnterText("TextEntryChanged", "When auto-resizing is enabled, the height of the Editor will increase when the user fills it with text, and the height will decrease as the user deletes text. This can be used to ensure that Editor objects in a DataTemplate.");
- App.WaitForElement("Apply");
- App.Tap("Apply");
- App.WaitForElement("TestEditor");
- VerifyScreenshotWithKeyboardHandling();
+ Assert.That(App.WaitForElement("TestEditor").GetText(), Is.EqualTo("test editor"));
}
#if TEST_FAILS_ON_WINDOWS //related issue link: https://github.com/dotnet/maui/issues/29812
- [Test]
- public void VerifyEditor_WithShadow()
+ [Test, Order(40)]
+ public void VerifyEditorWithShadow()
{
App.WaitForElement("Options");
App.Tap("Options");
@@ -665,7 +683,7 @@ public void VerifyEditor_WithShadow()
VerifyScreenshot(cropBottom: CropBottomValue);
}
- [Test]
+ [Test, Order(41)]
public void VerifyEditorPlaceholderWithShadow()
{
App.WaitForElement("Options");
@@ -685,7 +703,7 @@ public void VerifyEditorPlaceholderWithShadow()
#endif
#if TEST_FAILS_ON_WINDOWS //On Windows, the placeholder is not visible because its text alignment is reset to default values when navigating to the page. This issue occurs only in the Host App.
- [Test]
+ [Test, Order(42)]
public void VerifyEditorPlaceholderWithHorizontalAlignment()
{
App.WaitForElement("Options");
@@ -703,7 +721,7 @@ public void VerifyEditorPlaceholderWithHorizontalAlignment()
VerifyScreenshot(cropBottom: CropBottomValue);
}
- [Test]
+ [Test, Order(43)]
public void VerifyEditorPlaceholderWithVerticalAlignment()
{
App.WaitForElement("Options");
@@ -722,7 +740,7 @@ public void VerifyEditorPlaceholderWithVerticalAlignment()
}
#if TEST_FAILS_ON_WINDOWS //related issue link: https://github.com/dotnet/maui/issues/30071
- [Test]
+ [Test, Order(44)]
public void VerifyEditorPlaceholderWithCharacterSpacing()
{
App.WaitForElement("Options");
@@ -742,7 +760,7 @@ public void VerifyEditorPlaceholderWithCharacterSpacing()
}
#endif
- [Test]
+ [Test, Order(45)]
public void VerifyEditorPlaceholderWithFontFamily()
{
App.WaitForElement("Options");
@@ -760,7 +778,7 @@ public void VerifyEditorPlaceholderWithFontFamily()
VerifyScreenshot(cropBottom: CropBottomValue);
}
- [Test]
+ [Test, Order(46)]
public void VerifyEditorPlaceholderWithFontSize()
{
App.WaitForElement("Options");
@@ -779,7 +797,7 @@ public void VerifyEditorPlaceholderWithFontSize()
VerifyScreenshot(cropBottom: CropBottomValue);
}
- [Test]
+ [Test, Order(47)]
public void VerifyEditorPlaceholderWithFontAttributes()
{
App.WaitForElement("Options");
@@ -796,10 +814,55 @@ public void VerifyEditorPlaceholderWithFontAttributes()
App.WaitForElement("TestEditor");
VerifyScreenshot(cropBottom: CropBottomValue);
}
+#endif
+
+ [Test, Order(48)]
+ public void VerifyEditorWhenHeightRequestSet()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("HeightRequestEntry");
+ App.ClearText("HeightRequestEntry");
+ App.EnterText("HeightRequestEntry", "100");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElement("TestEditor");
+ VerifyScreenshot(cropBottom: CropBottomValue);
+ }
+
+ [Test, Order(49)]
+ public void VerifyEditorWhenWidthRequestSet()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("WidthRequestEntry");
+ App.ClearText("WidthRequestEntry");
+ App.EnterText("WidthRequestEntry", "100");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElement("TestEditor");
+ VerifyScreenshot(cropBottom: CropBottomValue);
+ }
+
+ [Test, Order(50)]
+ public void VerifyEditorWhenHeightAndWidthRequestSet()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("HeightRequestEntry");
+ App.ClearText("HeightRequestEntry");
+ App.EnterText("HeightRequestEntry", "100");
+ App.ClearText("WidthRequestEntry");
+ App.EnterText("WidthRequestEntry", "80");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElement("TestEditor");
+ VerifyScreenshot(cropBottom: CropBottomValue);
+ }
-#if TEST_FAILS_ON_IOS && TEST_FAILS_ON_CATALYST //related issue link: https://github.com/dotnet/maui/issues/30571
- [Test]
- public void VerifyzEditorPlaceholderWithAutoSizeDiabled()
+#if TEST_FAILS_ON_IOS && TEST_FAILS_ON_CATALYST && TEST_FAILS_ON_WINDOWS //related issue link: https://github.com/dotnet/maui/issues/30571 and the placeholder is not visible because its text alignment is reset to default values when navigating to the page. This issue occurs only in the Host App on windows.
+ [Test, Order(51)]
+ public void VerifyEditorPlaceholderWithAutoSizeDisabled()
{
App.WaitForElement("Options");
App.Tap("Options");
@@ -812,10 +875,28 @@ public void VerifyzEditorPlaceholderWithAutoSizeDiabled()
App.Tap("Apply");
App.WaitForElement("TestEditor");
VerifyScreenshot(cropBottom: CropBottomValue);
+ App.ClearText("TestEditor");
}
+#endif
- [Test]
- public void VerifyzEditorPlaceholderWithAutoSizeTextChanges()
+ [Test, Order(52)]
+ public void VerifyEditorTextWhenAutoSizeDisabled()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("TextEntryChanged");
+ App.ClearText("TextEntryChanged");
+ App.EnterText("TextEntryChanged", "When auto-resizing is enabled, the height of the Editor will increase when the user fills it with text, and the height will decrease as the user deletes text. This can be used to ensure that Editor objects in a DataTemplate.");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElement("TestEditor");
+ VerifyScreenshot(cropBottom: CropBottomValue);
+ App.ClearText("TestEditor");
+ }
+
+#if TEST_FAILS_ON_IOS && TEST_FAILS_ON_CATALYST && TEST_FAILS_ON_WINDOWS //related issue link: https://github.com/dotnet/maui/issues/30571 and the placeholder is not visible because its text alignment is reset to default values when navigating to the page. This issue occurs only in the Host App on windows.
+ [Test, Order(53)]
+ public void VerifyEditorPlaceholderWithAutoSizeTextChanges()
{
App.WaitForElement("Options");
App.Tap("Options");
@@ -830,8 +911,221 @@ public void VerifyzEditorPlaceholderWithAutoSizeTextChanges()
App.Tap("Apply");
App.WaitForElement("TestEditor");
VerifyScreenshot(cropBottom: CropBottomValue);
+ App.ClearText("TestEditor");
}
#endif
+
+ [Test, Order(54)]
+ public void VerifyEditorTextWhenAutoSizeTextChangesSet()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("AutoSizeTextChanges");
+ App.Tap("AutoSizeTextChanges");
+ App.WaitForElement("TextEntryChanged");
+ App.ClearText("TextEntryChanged");
+ App.EnterText("TextEntryChanged", "When auto-resizing is enabled, the height of the Editor will increase when the user fills it with text, and the height will decrease as the user deletes text. This can be used to ensure that Editor objects in a DataTemplate.");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ VerifyScreenshot(cropBottom: CropBottomValue);
+ App.ClearText("TestEditor");
+ }
+
+ [Test, Order(55)]
+ public void VerifyEditorTextWhenAutoSizeTextChangesSetWithShortShrinkText()
+ {
+ Exception? exception = null;
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("AutoSizeTextChanges");
+ App.Tap("AutoSizeTextChanges");
+ App.WaitForElement("TextEntryChanged");
+ App.ClearText("TextEntryChanged");
+#if MACCATALYST
+ App.EnterText("TextEntryChanged", "WhenautoresizingisenabledtheheightoftheEditorwillincreasewhentheuserfillsitwithtextandtheheightwilldecreaseastheuserdeletestextThiscanbeusedtoensurethatEditorobjectsinaDataTemplate");
+#else
+ App.EnterText("TextEntryChanged", "When auto-resizing is enabled, the height of the Editor will increase when the user fills it with text, and the height will decrease as the user deletes text. This can be used to ensure that Editor objects in a DataTemplate.");
+#endif
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElement("TestEditor");
+ VerifyScreenshotWithKeyboardHandlingOrSetException(ref exception, "VerifyEditorTextWhenAutoSizeTextChangesSetWithShortShrinkText_LongText");
+ App.ClearText("TestEditor");
+ App.EnterText("TestEditor", "Short text");
+ VerifyScreenshotWithKeyboardHandlingOrSetException(ref exception, "VerifyEditorTextWhenAutoSizeTextChangesSetWithShortShrinkText_ShortText");
+ if (exception != null)
+ {
+ throw exception;
+ }
+ }
+
+ [Test, Order(56)]
+ public void VerifyEditorTextWhenAutoSizeTextChangesSetWithHeightRequest()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("HeightRequestEntry");
+ App.ClearText("HeightRequestEntry");
+ App.EnterText("HeightRequestEntry", "100");
+ App.WaitForElement("AutoSizeTextChanges");
+ App.Tap("AutoSizeTextChanges");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElement("TestEditor");
+ VerifyScreenshot(cropBottom: CropBottomValue);
+ }
+
+ [Test, Order(57)]
+ public void VerifyEditorTextWhenFontAttributesBoldAndItalicSet()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("FontAttributesBold");
+ App.Tap("FontAttributesBold");
+ App.WaitForElement("FontAttributesItalic");
+ App.Tap("FontAttributesItalic");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElement("TestEditor");
+ VerifyScreenshot(cropBottom: CropBottomValue);
+ }
+
+ [Test, Order(58)]
+ public void VerifyEditorPlaceholderTextWhenFontAttributesBoldAndItalicSet()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("FontAttributesBold");
+ App.Tap("FontAttributesBold");
+ App.WaitForElement("FontAttributesItalic");
+ App.Tap("FontAttributesItalic");
+ App.WaitForElement("PlaceholderText");
+ App.ClearText("PlaceholderText");
+ App.EnterText("PlaceholderText", "Enter your name");
+ App.WaitForElement("TextEntryChanged");
+ App.ClearText("TextEntryChanged");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElement("TestEditor");
+ VerifyScreenshot(cropBottom: CropBottomValue);
+ }
+
+ [Test, Order(59)]
+ public void VerifyEditorWhenOpacitySet()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("OpacityEntry");
+ App.ClearText("OpacityEntry");
+ App.EnterText("OpacityEntry", "0.5");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElement("TestEditor");
+ VerifyScreenshot(cropBottom: CropBottomValue);
+ }
+
+ [Test, Order(60)]
+ public void VerifyEditorWhenOpacityResetToDefault()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("OpacityEntry");
+ App.ClearText("OpacityEntry");
+ App.EnterText("OpacityEntry", "0.5");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElement("TestEditor");
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("OpacityEntry");
+ App.ClearText("OpacityEntry");
+ App.EnterText("OpacityEntry", "1.0");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElement("TestEditor");
+ VerifyScreenshot(cropBottom: CropBottomValue);
+ }
+
+ [Test, Order(61)]
+ public void VerifyEditorWhenOpacitySetToZero()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("OpacityEntry");
+ App.ClearText("OpacityEntry");
+ App.EnterText("OpacityEntry", "0");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElement("EditorControlTitleLabel");
+ VerifyScreenshot(cropBottom: CropBottomValue);
+ }
+
+ [Test, Order(62)]
+ public void VerifyEditorWhenBackgroundColorSet()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("BackgroundColorYellow");
+ App.Tap("BackgroundColorYellow");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElement("TestEditor");
+ VerifyScreenshot(cropBottom: CropBottomValue);
+ }
+
+ [Test, Order(63)]
+ public void VerifyEditorBackgroundColorWithTextColor()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("BackgroundColorLightBlue");
+ App.Tap("BackgroundColorLightBlue");
+ App.WaitForElement("TextColorRed");
+ App.Tap("TextColorRed");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElement("TestEditor");
+ VerifyScreenshot(cropBottom: CropBottomValue);
+ }
+
+ [Test, Order(64)]
+ public void VerifyEditorBackgroundColorWithPlaceholder()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("BackgroundColorLightBlue");
+ App.Tap("BackgroundColorLightBlue");
+ App.WaitForElement("PlaceholderText");
+ App.ClearText("PlaceholderText");
+ App.EnterText("PlaceholderText", "Enter your name");
+ App.WaitForElement("TextEntryChanged");
+ App.ClearText("TextEntryChanged");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElement("TestEditor");
+ VerifyScreenshot(cropBottom: CropBottomValue);
+ }
+
+#if TEST_FAILS_ON_CATALYST && TEST_FAILS_ON_IOS //related issue link: https://github.com/dotnet/maui/issues/34611
+ [Test, Order(65)]
+ public void VerifyEditorBackgroundColorResetToNone()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("BackgroundColorYellow");
+ App.Tap("BackgroundColorYellow");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElement("TestEditor");
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("BackgroundColorNone");
+ App.Tap("BackgroundColorNone");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElement("TestEditor");
+ VerifyScreenshot(cropBottom: CropBottomValue);
+ }
#endif
///
@@ -851,4 +1145,19 @@ private void VerifyScreenshotWithKeyboardHandling(string? screenshotName = null)
else
VerifyScreenshot(screenshotName, cropBottom: CropBottomValue);
}
-}
\ No newline at end of file
+
+ ///
+ /// Helper method to handle keyboard visibility and set exception if screenshot verification fails
+ ///
+ /// Reference to exception variable
+ /// Name for the screenshot
+ private void VerifyScreenshotWithKeyboardHandlingOrSetException(ref Exception? exception, string screenshotName)
+ {
+#if ANDROID
+ if (App.IsKeyboardShown())
+ App.DismissKeyboard();
+#endif
+ VerifyScreenshotOrSetException(ref exception, screenshotName, cropBottom: CropBottomValue);
+
+ }
+}
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/LabelFeatureTests.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/LabelFeatureTests.cs
index 3bbc6fb84eed..03a36721b2ad 100644
--- a/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/LabelFeatureTests.cs
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/LabelFeatureTests.cs
@@ -698,6 +698,7 @@ public void VerifyLabelWithTextWhenPaddingApplied()
VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
}
+#if TEST_FAILS_ON_CATALYST // Issue Link: https://github.com/dotnet/maui/issues/37117
[Test, Order(37)]
[Category(UITestCategories.Label)]
public void VerifyLabelWithTextAndMaxlines()
@@ -716,6 +717,7 @@ public void VerifyLabelWithTextAndMaxlines()
App.Tap(MainLabel);
VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
}
+#endif
[Test, Order(59)]
[Category(UITestCategories.Label)]
@@ -736,6 +738,7 @@ public void VerifyLabelWithTextWhenLineHeight()
VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
}
+#if TEST_FAILS_ON_CATALYST // Issue Link: https://github.com/dotnet/maui/issues/37117
[Test, Order(38)]
[Category(UITestCategories.Label)]
public void VerifyLabelWithTextAndLineBreakModeNoWrap()
@@ -784,6 +787,7 @@ public void VerifyLabelWithTextAndLineBreakModeMiddleTruncation()
App.Tap(Apply);
VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
}
+#endif
#endif
[Test, Order(45)]
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/Material3RadioButtonFeatureTests.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/Material3RadioButtonFeatureTests.cs
index e42d1667a7e0..4c26bf5835ad 100644
--- a/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/Material3RadioButtonFeatureTests.cs
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/Material3RadioButtonFeatureTests.cs
@@ -9,6 +9,7 @@
namespace Microsoft.Maui.TestCases.Tests;
+[Category(UITestCategories.Material3)]
public class Material3RadioButtonFeatureTests : _GalleryUITest
{
public override string GalleryPageName => "RadioButton Feature Matrix";
@@ -19,7 +20,6 @@ public Material3RadioButtonFeatureTests(TestDevice device)
}
[Test, Order(1)]
- [Category(UITestCategories.Material3)]
public void Material3RadioButton_Checking_Default_Configuration_VerifyVisualState()
{
App.WaitForElement("RadioButtonControlOne");
@@ -27,7 +27,6 @@ public void Material3RadioButton_Checking_Default_Configuration_VerifyVisualStat
}
[Test, Order(2)]
- [Category(UITestCategories.Material3)]
public void Material3RadioButton_Checking_Initial_Configuration_VerifyVisualState()
{
App.WaitForElement("RadioButtonControlOne");
@@ -41,9 +40,8 @@ public void Material3RadioButton_Checking_Initial_Configuration_VerifyVisualStat
VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
}
-#if TEST_FAILS_ON_ANDROID // This test fails on Android because the RadioButton control does not update the BorderColor at runtime. Issue Link - https://github.com/dotnet/maui/issues/15806
- [Test]
- [Category(UITestCategories.Material3)]
+#if TEST_FAILS_ON_ANDROID // This test fails on Android because the RadioButton control does not update the BorderColor at runtime. Issue Link - https://github.com/dotnet/maui/issues/35587
+ [Test, Order(3)]
public void Material3RadioButton_SetTextColorAndBorderColor_VerifyVisualState()
{
App.WaitForElement("Options");
@@ -63,8 +61,7 @@ public void Material3RadioButton_SetTextColorAndBorderColor_VerifyVisualState()
}
#endif
- [Test]
- [Category(UITestCategories.Material3)]
+ [Test, Order(4)]
public void Material3RadioButton_SetFontAttributesAndTextColor_VerifyVisualState()
{
App.WaitForElement("Options");
@@ -79,8 +76,7 @@ public void Material3RadioButton_SetFontAttributesAndTextColor_VerifyVisualState
VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
}
- [Test]
- [Category(UITestCategories.Material3)]
+ [Test, Order(5)]
public void Material3RadioButton_SetFontFamilyAndFontSize_VerifyVisualState()
{
App.WaitForElement("Options");
@@ -97,9 +93,8 @@ public void Material3RadioButton_SetFontFamilyAndFontSize_VerifyVisualState()
VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
}
-#if TEST_FAILS_ON_ANDROID // This test fails on Android because the RadioButton control does not update the BorderColor at runtime. Issue Link - https://github.com/dotnet/maui/issues/15806
- [Test]
- [Category(UITestCategories.Material3)]
+#if TEST_FAILS_ON_ANDROID // This test fails on Android because the RadioButton control does not update the BorderColor at runtime. Issue Link - https://github.com/dotnet/maui/issues/35587
+ [Test, Order(6)]
public void Material3RadioButton_SetBorderWidthAndCornerRadius_VerifyVisualState()
{
App.WaitForElement("Options");
@@ -121,9 +116,7 @@ public void Material3RadioButton_SetBorderWidthAndCornerRadius_VerifyVisualState
}
#endif
-#if TEST_FAILS_ON_ANDROID // This test fails on Android because the text transform is not applied correctly. Issue Link - https://github.com/dotnet/maui/issues/29729
- [Test]
- [Category(UITestCategories.Material3)]
+ [Test, Order(7)]
public void Material3RadioButton_SetFontFamilyAndTextTransform_VerifyVisualState()
{
App.WaitForElement("Options");
@@ -137,11 +130,10 @@ public void Material3RadioButton_SetFontFamilyAndTextTransform_VerifyVisualState
App.WaitForElementTillPageNavigationSettled("RadioButtonControlOne");
VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
}
-#endif
+
#if TEST_FAILS_ON_ANDROID // On Android, the View object is not supported, so it falls back to a string representation of the object. https://learn.microsoft.com/en-us/dotnet/maui/user-interface/controls/radiobutton?view=net-maui-9.0#create-radiobuttons
- [Test]
- [Category(UITestCategories.Material3)]
+ [Test, Order(8)]
public void Material3RadioButton_SetContentWithView()
{
App.WaitForElement("Options");
@@ -155,8 +147,7 @@ public void Material3RadioButton_SetContentWithView()
}
#endif
- [Test]
- [Category(UITestCategories.Material3)]
+ [Test, Order(9)]
public void Material3RadioButton_SetContentAndTextColor_VerifyVisualState()
{
App.WaitForElement("Options");
@@ -173,8 +164,7 @@ public void Material3RadioButton_SetContentAndTextColor_VerifyVisualState()
VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
}
- [Test]
- [Category(UITestCategories.Material3)]
+ [Test, Order(10)]
public void Material3RadioButton_SetContentAndCharacterSpacing_VerifyVisualState()
{
App.WaitForElement("Options");
@@ -193,8 +183,7 @@ public void Material3RadioButton_SetContentAndCharacterSpacing_VerifyVisualState
VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
}
- [Test]
- [Category(UITestCategories.Material3)]
+ [Test, Order(11)]
public void Material3RadioButton_SetContentAndFontSize_VerifyVisualState()
{
App.WaitForElement("Options");
@@ -215,8 +204,7 @@ public void Material3RadioButton_SetContentAndFontSize_VerifyVisualState()
VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
}
- [Test]
- [Category(UITestCategories.Material3)]
+ [Test, Order(12)]
public void Material3RadioButton_SetContentAndFontAttributes_VerifyVisualState()
{
App.WaitForElement("Options");
@@ -233,9 +221,7 @@ public void Material3RadioButton_SetContentAndFontAttributes_VerifyVisualState()
VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
}
-#if TEST_FAILS_ON_ANDROID // This test fails on Android because the text transform is not applied correctly. Issue Link - https://github.com/dotnet/maui/issues/29729
- [Test]
- [Category(UITestCategories.Material3)]
+ [Test, Order(13)]
public void Material3RadioButton_SetContentAndTextTransform()
{
App.WaitForElement("Options");
@@ -250,10 +236,8 @@ public void Material3RadioButton_SetContentAndTextTransform()
App.Tap("Apply");
VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
}
-#endif
- [Test]
- [Category(UITestCategories.Material3)]
+ [Test, Order(14)]
public void Material3RadioButton_SetFontFamilyAndFontAttributes_VerifyVisualState()
{
App.WaitForElement("Options");
@@ -268,8 +252,7 @@ public void Material3RadioButton_SetFontFamilyAndFontAttributes_VerifyVisualStat
VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
}
- [Test]
- [Category(UITestCategories.Material3)]
+ [Test, Order(15)]
public void Material3RadioButton_SetFontSizeAndFontAttributes_VerifyVisualState()
{
App.WaitForElement("Options");
@@ -286,8 +269,7 @@ public void Material3RadioButton_SetFontSizeAndFontAttributes_VerifyVisualState(
VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
}
- [Test]
- [Category(UITestCategories.Material3)]
+ [Test, Order(16)]
public void Material3RadioButton_IsVisibleAndContent_VerifyVisualState()
{
App.WaitForElement("Options");
@@ -303,8 +285,7 @@ public void Material3RadioButton_IsVisibleAndContent_VerifyVisualState()
App.WaitForNoElement("RadioButtonControlOne");
}
- [Test]
- [Category(UITestCategories.Material3)]
+ [Test, Order(17)]
public void Material3RadioButton_FlowDirectionAndContent_VerifyVisualState()
{
App.WaitForElement("Options");
@@ -320,5 +301,22 @@ public void Material3RadioButton_FlowDirectionAndContent_VerifyVisualState()
App.WaitForElementTillPageNavigationSettled("RadioButtonControlOne");
VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
}
+
+ [Test, Order(18)]
+ public void Material3RadioButton_SetFontAutoScalingEnabled_VerifyVisualState()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("FontAutoScalingEnabledFalseRadio");
+ App.Tap("FontAutoScalingEnabledFalseRadio");
+ App.WaitForElement("FontSizeEntry");
+ App.ClearText("FontSizeEntry");
+ App.EnterText("FontSizeEntry", "20");
+ App.PressEnter();
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElementTillPageNavigationSettled("RadioButtonControlOne");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
}
#endif
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/MenuBarItemFeatureTests.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/MenuBarItemFeatureTests.cs
new file mode 100644
index 000000000000..fa33e17b49fe
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/MenuBarItemFeatureTests.cs
@@ -0,0 +1,536 @@
+// This feature test is applicable only on desktop platforms (Windows and Mac).
+#if MACCATALYST || WINDOWS
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests;
+
+[Category(UITestCategories.Shell)]
+public class MenuBarItemFeatureTests : _GalleryUITest
+{
+ public const string MenuBarItemFeatureMatrix = "MenuBarItem Feature Matrix";
+ public override string GalleryPageName => MenuBarItemFeatureMatrix;
+
+ public MenuBarItemFeatureTests(TestDevice device)
+ : base(device)
+ {
+ }
+
+
+#if WINDOWS
+ [Test, Order(1)]
+ public void MenuBarItem_FileMenuExit()
+ {
+ App.WaitForElement("ResetButton");
+ App.Tap("ResetButton");
+
+ // Verify status message reset
+ var statusLabel = App.FindElement("StatusMessageLabel");
+ Assert.That(statusLabel.GetText(), Does.Contain("reset"));
+
+ // Open File menu and click Exit
+ App.WaitForElement("FileMenuBar");
+ App.Tap("FileMenuBar");
+
+ VerifyScreenshot();
+ }
+
+ [Test, Order(2)]
+ public void MenuBarItem_RefreshMenuItemProperties()
+ {
+ App.WaitForElement("ResetButton");
+ App.Tap("ResetButton");
+
+ // Open View menu
+ App.WaitForElement("ViewMenuBar");
+ App.Tap("ViewMenuBar");
+
+ VerifyScreenshot();
+ }
+ [Test, Order(3)]
+ public void MenuBarItem_MenuFlyoutSeparatorPresent()
+ {
+ App.WaitForElement("ResetButton");
+ App.Tap("ResetButton");
+
+ // Open Locations menu which has a separator
+ App.WaitForElement("LocationsMenuBar");
+ App.Tap("LocationsMenuBar");
+
+ // Verify menu items before and after separator are present
+ App.WaitForElement("Change Location");
+ App.WaitForElement("Add Location");
+ App.WaitForElement("Edit Location");
+ App.WaitForElement("Remove Location");
+
+ // Take screenshot to verify separator visual appearance
+ VerifyScreenshot();
+ }
+
+ [Test, Order(4)]
+ public void MenuBarItem_MediaMenuBarItemPresent()
+ {
+ App.WaitForElement("ResetButton");
+ App.Tap("ResetButton");
+
+ // Open Locations menu which has a separator
+ App.WaitForElement("MediaMenuBar");
+ App.Tap("MediaMenuBar");
+
+ // Take screenshot to verify separator visual appearance
+ VerifyScreenshot();
+ }
+#endif
+
+
+
+ [Test, Order(5)]
+ public void MenuBarItem_LocationsMenuChangeLocation()
+ {
+ App.WaitForElement("ResetButton");
+ App.Tap("ResetButton");
+
+ // Verify initial location
+ var locationLabel = App.FindElement("CurrentLocationLabel");
+ Assert.That(locationLabel.GetText(), Is.EqualTo("Not set"));
+
+ // Open Locations menu
+ App.WaitForElement("LocationsMenuBar");
+ App.Tap("LocationsMenuBar");
+
+ // Open Change Location submenu
+ App.WaitForElement("Change Location");
+ App.Tap("Change Location");
+
+ // Select first location (Redmond, USA)
+ // Note: Dynamic menu items may not have AutomationIds, need to tap by text
+ App.WaitForElement("Redmond, USA");
+ App.Tap("Redmond, USA");
+
+ // Verify location changed
+ var updatedLocation = App.FindElement("CurrentLocationLabel");
+ Assert.That(updatedLocation.GetText(), Is.EqualTo("Redmond, USA"));
+
+ }
+
+ [Test, Order(6)]
+ public void MenuBarItem_LocationsMenuAddLocation()
+ {
+ App.WaitForElement("ResetButton");
+ App.Tap("ResetButton");
+
+ // Open Locations menu
+ App.WaitForElement("LocationsMenuBar");
+ App.Tap("LocationsMenuBar");
+
+ // Click Add Location
+ App.WaitForElement("Add Location");
+ App.Tap("Add Location");
+
+ App.WaitForElement("LocationEntry");
+ App.ClearText("LocationEntry");
+ App.EnterText("LocationEntry", "Tokyo, JP");
+
+ App.WaitForElement("ConfirmButton");
+ App.Tap("ConfirmButton");
+
+ // Verify new location added to collection
+ App.FindElementByText("Tokyo, JP");
+
+ }
+
+ [Test, Order(7)]
+ public void MenuBarItem_LocationsMenuEditLocation()
+ {
+ App.WaitForElement("ResetButton");
+ App.Tap("ResetButton");
+
+ App.WaitForElement("LocationCheckBox_0");
+ App.Tap("LocationCheckBox_0"); // Select Redmond, USA
+
+ // Open Locations menu
+ App.WaitForElement("LocationsMenuBar");
+ App.Tap("LocationsMenuBar");
+
+ // Click Edit Location
+ App.WaitForElement("Edit Location");
+ App.Tap("Edit Location");
+
+ App.WaitForElement("LocationEntry");
+ App.ClearText("LocationEntry");
+ App.EnterText("LocationEntry", "Seattle, USA");
+
+ App.WaitForElement("ConfirmButton");
+ App.Tap("ConfirmButton");
+
+ App.FindElementByText("Seattle, USA");
+
+ }
+
+ [Test, Order(8)]
+ public void MenuBarItem_LocationsMenuRemoveLocation()
+ {
+ App.WaitForElement("ResetButton");
+ App.Tap("ResetButton");
+
+ App.WaitForElement("LocationCheckBox_2");
+ App.Tap("LocationCheckBox_2"); // Select Berlin, DE
+
+ // Open Locations menu
+ App.WaitForElement("LocationsMenuBar");
+ App.Tap("LocationsMenuBar");
+
+ // Click Remove Location
+ App.WaitForElement("Remove Location");
+ App.Tap("Remove Location");
+
+ var locationLabel = App.FindElement("StatusMessageLabel");
+ Assert.That(locationLabel.GetText(), Is.EqualTo("Removed location: Berlin, DE"));
+
+ }
+
+ [Test, Order(9)]
+ public void MenuBarItem_ViewMenuRefreshCommand()
+ {
+ App.WaitForElement("ResetButton");
+ App.Tap("ResetButton");
+
+ // Open View menu
+ App.WaitForElement("ViewMenuBar");
+ App.Tap("ViewMenuBar");
+
+ // Click Refresh
+ App.WaitForElement("RefreshMenuBarFlyoutItem");
+ App.Tap("RefreshMenuBarFlyoutItem");
+
+ // Verify status message shows timestamp
+ var statusLabel = App.FindElement("StatusMessageLabel");
+ Assert.That(statusLabel.GetText(), Does.Contain("Refreshed"));
+ }
+
+#if TEST_FAILS_ON_CATALYST //For more info, see: https://github.com/dotnet/maui/issues/34038
+ [Test, Order(10)]
+ public void MenuBarItem_DisableFileMenu()
+ {
+ App.WaitForElement("ResetButton");
+ App.Tap("ResetButton");
+
+ // Disable File menu
+ App.WaitForElement("FileMenuEnabledSwitch");
+ App.Tap("FileMenuEnabledSwitch");
+
+ // Try to open File menu
+ App.WaitForElement("FileMenuBar");
+ App.Tap("FileMenuBar");
+
+ // Verify "Exit" menu item is not accessible when menu is disabled
+ var elements = App.FindElements("ExitMenuBarFlyoutItem");
+ Assert.That(elements, Is.Empty, "Disabled menu items should not be accessible");
+ }
+
+ [Test, Order(11)]
+ public void MenuBarItem_DisableLocationsMenu()
+ {
+ App.WaitForElement("ResetButton");
+ App.Tap("ResetButton");
+
+ // Disable Locations menu
+ App.WaitForElement("LocationsMenuEnabledSwitch");
+ App.Tap("LocationsMenuEnabledSwitch");
+
+ // Try to open Locations menu
+ App.WaitForElement("LocationsMenuBar");
+ App.Tap("LocationsMenuBar");
+
+ // Verify "Add Location" menu item is not accessible when menu is disabled
+ var elements = App.FindElements("AddLocationMenuFlyoutItem");
+ Assert.That(elements, Is.Empty, "Disabled menu items should not be accessible");
+ }
+
+ [Test, Order(12)]
+ public void MenuBarItem_DisableViewMenu()
+ {
+ App.WaitForElement("ResetButton");
+ App.Tap("ResetButton");
+
+ // Disable View menu
+ App.WaitForElement("ViewMenuEnabledSwitch");
+ App.Tap("ViewMenuEnabledSwitch");
+
+ // Try to open View menu
+ App.WaitForElement("ViewMenuBar");
+ App.Tap("ViewMenuBar");
+
+ // Verify "Refresh" menu item is not accessible when menu is disabled
+ var elements = App.FindElements("RefreshMenuBarFlyoutItem");
+ Assert.That(elements, Is.Empty, "Disabled menu items should not be accessible");
+ }
+#endif
+
+ [Test, Order(13)]
+ public void MenuBarItem_VerifyAllMenusAndItemsAccessible()
+ {
+ App.WaitForElement("ResetButton");
+ App.Tap("ResetButton");
+
+ App.WaitForElement("FileMenuBar");
+ App.Tap("FileMenuBar");
+ App.WaitForElement("ExitMenuBarFlyoutItem");
+
+ App.WaitForElement("ViewMenuBarItem");
+ App.Tap("ViewMenuBarItem");
+
+ App.WaitForElement("LocationsMenuBar");
+ App.Tap("LocationsMenuBar");
+ App.WaitForElement("Change Location");
+ App.WaitForElement("Add Location");
+ App.WaitForElement("Edit Location");
+ App.WaitForElement("Remove Location");
+
+ App.WaitForElement("ViewMenuBarItem");
+ App.Tap("ViewMenuBarItem");
+
+ App.WaitForElement("ViewMenuBar");
+ App.Tap("ViewMenuBar");
+ App.WaitForElement("RefreshMenuBarFlyoutItem");
+
+ App.WaitForElement("ViewMenuBarItem");
+ App.Tap("ViewMenuBarItem");
+
+ App.WaitForElement("MediaMenuBar");
+ App.Tap("MediaMenuBar");
+ App.WaitForElement("Play");
+ App.WaitForElement("Pause");
+ App.WaitForElement("Stop");
+ }
+
+ [Test, Order(14)]
+ public void MenuBarItem_DynamicLocationMenuItems()
+ {
+ App.WaitForElement("ResetButton");
+ App.Tap("ResetButton");
+
+ // Open Locations menu and Change Location submenu
+ App.WaitForElement("LocationsMenuBar");
+ App.Tap("LocationsMenuBar");
+
+ App.WaitForElement("Change Location");
+ App.Tap("Change Location");
+
+ // Verify all three default locations exist
+ App.WaitForElement("Redmond, USA");
+ App.WaitForElement("London, UK");
+ App.WaitForElement("Berlin, DE");
+
+ // Select one location
+ App.Tap("London, UK");
+
+ // Verify location changed
+ var locationLabel = App.FindElement("CurrentLocationLabel");
+ Assert.That(locationLabel.GetText(), Is.EqualTo("London, UK"));
+ }
+
+ [Test, Order(15)]
+ public void MenuBarItem_VerifyAllMenusPresent()
+ {
+ App.WaitForElement("ResetButton");
+ App.Tap("ResetButton");
+
+ // Verify all three menu bar items are present
+ App.WaitForElement("FileMenuBar");
+ App.WaitForElement("LocationsMenuBar");
+ App.WaitForElement("ViewMenuBar");
+ App.WaitForElement("MediaMenuBar");
+
+ // Verify status labels are present
+ App.WaitForElement("CurrentLocationLabel");
+ App.WaitForElement("StatusMessageLabel");
+
+ // Verify control switches are present
+ App.WaitForElement("FileMenuEnabledSwitch");
+ App.WaitForElement("LocationsMenuEnabledSwitch");
+ App.WaitForElement("ViewMenuEnabledSwitch");
+
+ // Verify locations collection is present
+ App.WaitForElement("LocationsCollectionView");
+ }
+
+ [Test, Order(16)]
+ public void MenuBarItem_AddMultipleLocations()
+ {
+ App.WaitForElement("ResetButton");
+ App.Tap("ResetButton");
+
+ // Add first location
+ App.WaitForElement("LocationsMenuBar");
+ App.Tap("LocationsMenuBar");
+ App.WaitForElement("Add Location");
+ App.Tap("Add Location");
+
+ App.WaitForElement("LocationEntry");
+ App.ClearText("LocationEntry");
+ App.EnterText("LocationEntry", "Tokyo, JP");
+ App.Tap("ConfirmButton");
+
+ // Add second location
+ App.WaitForElement("LocationsMenuBar");
+ App.Tap("LocationsMenuBar");
+ App.WaitForElement("Add Location");
+ App.Tap("Add Location");
+
+ App.WaitForElement("LocationEntry");
+ App.ClearText("LocationEntry");
+ App.EnterText("LocationEntry", "Paris, FR");
+ App.Tap("ConfirmButton");
+
+ // Verify both locations added
+ App.WaitForElement("Tokyo, JP");
+ App.WaitForElement("Paris, FR");
+ }
+
+ [Test, Order(17)]
+ public void MenuBarItem_CancelAddLocation()
+ {
+ App.WaitForElement("ResetButton");
+ App.Tap("ResetButton");
+
+ // Start adding location
+ App.WaitForElement("LocationsMenuBar");
+ App.Tap("LocationsMenuBar");
+ App.WaitForElement("Add Location");
+ App.Tap("Add Location");
+
+ // Enter text but cancel
+ App.WaitForElement("LocationEntry");
+ App.EnterText("LocationEntry", "Cancelled Location");
+ App.Tap("CancelButton");
+
+ // Verify location was not added
+ App.WaitForElement("Operation cancelled");
+ }
+
+ [Test, Order(18)]
+ public void MenuBarItem_ResetRestoresDefaultLocations()
+ {
+ App.WaitForElement("ResetButton");
+ App.Tap("ResetButton");
+
+ // Add a new location
+ App.WaitForElement("LocationsMenuBar");
+ App.Tap("LocationsMenuBar");
+ App.WaitForElement("Add Location");
+ App.Tap("Add Location");
+
+ App.WaitForElement("LocationEntry");
+ App.EnterText("LocationEntry", "Custom Location");
+ App.Tap("ConfirmButton");
+
+ // Verify custom location was added
+ var locationsCollection = App.WaitForElement("LocationsCollectionView");
+ App.WaitForElement("Custom Location");
+
+ // Reset
+ App.Tap("ResetButton");
+
+ // Verify only default locations remain
+ App.WaitForElement("Redmond, USA");
+ App.WaitForElement("London, UK");
+ App.WaitForElement("Berlin, DE");
+ App.WaitForNoElement("Custom Location");
+ }
+
+ [Test, Order(19)]
+ public void MenuBarItem_ToggleMenusOnOff()
+ {
+ App.WaitForElement("ResetButton");
+ App.Tap("ResetButton");
+
+ // Toggle File menu off then on
+ App.WaitForElement("FileMenuEnabledSwitch");
+ App.Tap("FileMenuEnabledSwitch"); // Off
+ App.WaitForElement("FileMenuEnabledSwitch");
+ App.Tap("FileMenuEnabledSwitch"); // On
+
+ // Toggle Locations menu off then on
+ App.WaitForElement("LocationsMenuEnabledSwitch");
+ App.Tap("LocationsMenuEnabledSwitch"); // Off
+ App.WaitForElement("LocationsMenuEnabledSwitch");
+ App.Tap("LocationsMenuEnabledSwitch"); // On
+
+ // Toggle View menu off then on
+ App.WaitForElement("ViewMenuEnabledSwitch");
+ App.Tap("ViewMenuEnabledSwitch"); // Off
+ App.WaitForElement("ViewMenuEnabledSwitch");
+ App.Tap("ViewMenuEnabledSwitch"); // On
+
+ // Verify all menus are still present after toggling
+ App.WaitForElement("FileMenuBar");
+ App.WaitForElement("LocationsMenuBar");
+ App.WaitForElement("ViewMenuBar");
+ }
+
+ [Test, Order(20)]
+ public void MenuBarItem_VerifyInitialLocationState()
+ {
+ App.WaitForElement("ResetButton");
+ App.Tap("ResetButton");
+
+ // Verify initial location is "Not set"
+ var locationLabel = App.FindElement("CurrentLocationLabel");
+ Assert.That(locationLabel.GetText(), Is.EqualTo("Not set"));
+
+ // Verify default locations in collection
+ var locationsCollection = App.WaitForElement("LocationsCollectionView");
+ App.WaitForElement("Redmond, USA");
+ App.WaitForElement("London, UK");
+ App.WaitForElement("Berlin, DE");
+ }
+
+ [Test, Order(21)]
+ public void MenuBarItem_EntryVisibilityToggling()
+ {
+ App.WaitForElement("ResetButton");
+ App.Tap("ResetButton");
+
+ // Open Add Location (should make entry visible)
+ App.WaitForElement("LocationsMenuBar");
+ App.Tap("LocationsMenuBar");
+ App.WaitForElement("Add Location");
+ App.Tap("Add Location");
+
+ // Verify entry is visible
+ App.WaitForElement("LocationEntry");
+ App.WaitForElement("ConfirmButton");
+ App.WaitForElement("CancelButton");
+
+ // Cancel (should hide entry)
+ App.Tap("CancelButton");
+
+ // Note: Entry visibility check would require checking if element is displayed
+ // The entry should be hidden after cancel
+ App.WaitForNoElement("LocationEntry");
+ }
+
+ [Test, Order(22)]
+ public void MenuBarItem_AddEmptyLocationValidation()
+ {
+ App.WaitForElement("ResetButton");
+ App.Tap("ResetButton");
+
+ // Try to add empty location
+ App.WaitForElement("LocationsMenuBar");
+ App.Tap("LocationsMenuBar");
+ App.WaitForElement("Add Location");
+ App.Tap("Add Location");
+
+ App.WaitForElement("LocationEntry");
+ // Don't enter any text, just confirm
+ App.Tap("ConfirmButton");
+
+ // Verify validation message
+ var statusLabel = App.FindElement("StatusMessageLabel");
+ Assert.That(statusLabel.GetText(), Does.Contain("cannot be empty").Or.Contain("empty"));
+ }
+}
+#endif
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/RadioButtonFeatureTests.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/RadioButtonFeatureTests.cs
index 68a64f622745..33fc698800c9 100644
--- a/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/RadioButtonFeatureTests.cs
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/RadioButtonFeatureTests.cs
@@ -4,6 +4,7 @@
namespace Microsoft.Maui.TestCases.Tests;
+[Category(UITestCategories.RadioButton)]
public class RadioButtonFeatureTests : _GalleryUITest
{
public const string RadioButtonFeatureMatrix = "RadioButton Feature Matrix";
@@ -16,7 +17,6 @@ public RadioButtonFeatureTests(TestDevice device)
}
[Test, Order(1)]
- [Category(UITestCategories.RadioButton)]
public void RadioButton_Checking_Default_Configuration_VerifyVisualState()
{
App.WaitForElement("RadioButtonControlOne");
@@ -24,8 +24,7 @@ public void RadioButton_Checking_Default_Configuration_VerifyVisualState()
}
[Test, Order(2)]
- [Category(UITestCategories.RadioButton)]
- public void RadioButton_Checking_Initial_Configuration_VerifyVisualState()
+ public void RadioButton_Checking_Initial_Configuration_UpdatesSelectedValueLabels()
{
App.WaitForElement("RadioButtonControlOne");
App.Tap("RadioButtonControlOne");
@@ -35,12 +34,10 @@ public void RadioButton_Checking_Initial_Configuration_VerifyVisualState()
App.Tap("RadioButtonControlFour");
App.WaitForElement("SelectedValueLabelTwo");
Assert.That(App.WaitForElement("SelectedValueLabelTwo").GetText(), Is.EqualTo("All Notifications"));
- VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
}
#if TEST_FAILS_ON_WINDOWS && TEST_FAILS_ON_ANDROID // This test fails on Windows and Android because the RadioButton control does not update the BorderColor at runtime. Issue Link - https://github.com/dotnet/maui/issues/15806
- [Test]
- [Category(UITestCategories.RadioButton)]
+ [Test, Order(3)]
public void RadioButton_SetTextColorAndBorderColor_VerifyVisualState()
{
App.WaitForElement("Options");
@@ -60,8 +57,7 @@ public void RadioButton_SetTextColorAndBorderColor_VerifyVisualState()
}
#endif
- [Test]
- [Category(UITestCategories.RadioButton)]
+ [Test, Order(4)]
public void RadioButton_SetFontAttributesAndTextColor_VerifyVisualState()
{
App.WaitForElement("Options");
@@ -76,8 +72,7 @@ public void RadioButton_SetFontAttributesAndTextColor_VerifyVisualState()
VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
}
- [Test]
- [Category(UITestCategories.RadioButton)]
+ [Test, Order(5)]
public void RadioButton_SetFontFamilyAndFontSize_VerifyVisualState()
{
App.WaitForElement("Options");
@@ -95,8 +90,7 @@ public void RadioButton_SetFontFamilyAndFontSize_VerifyVisualState()
}
#if TEST_FAILS_ON_WINDOWS && TEST_FAILS_ON_ANDROID // This test fails on Windows and Android because the RadioButton control does not update the BorderColor at runtime. Issue Link - https://github.com/dotnet/maui/issues/15806
- [Test]
- [Category(UITestCategories.RadioButton)]
+ [Test, Order(6)]
public void RadioButton_SetBorderWidthAndCornerRadius_VerifyVisualState()
{
App.WaitForElement("Options");
@@ -118,9 +112,7 @@ public void RadioButton_SetBorderWidthAndCornerRadius_VerifyVisualState()
}
#endif
-#if TEST_FAILS_ON_ANDROID && TEST_FAILS_ON_WINDOWS // This test fails on Android and Windows because the text transform is not applied correctly. Issue Link - https://github.com/dotnet/maui/issues/29729
- [Test]
- [Category(UITestCategories.RadioButton)]
+ [Test, Order(7)]
public void RadioButton_SetFontFamilyAndTextTransform_VerifyVisualState()
{
App.WaitForElement("Options");
@@ -134,11 +126,9 @@ public void RadioButton_SetFontFamilyAndTextTransform_VerifyVisualState()
App.WaitForElementTillPageNavigationSettled("RadioButtonControlOne");
VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
}
-#endif
#if TEST_FAILS_ON_ANDROID // On Android, the View object is not supported, so it falls back to a string representation of the object. https://learn.microsoft.com/en-us/dotnet/maui/user-interface/controls/radiobutton?view=net-maui-9.0#create-radiobuttons
- [Test]
- [Category(UITestCategories.RadioButton)]
+ [Test, Order(8)]
public void RadioButton_SetContentWithView()
{
App.WaitForElement("Options");
@@ -152,8 +142,7 @@ public void RadioButton_SetContentWithView()
}
#endif
- [Test]
- [Category(UITestCategories.RadioButton)]
+ [Test, Order(9)]
public void RadioButton_SetContentAndTextColor_VerifyVisualState()
{
App.WaitForElement("Options");
@@ -171,8 +160,7 @@ public void RadioButton_SetContentAndTextColor_VerifyVisualState()
}
#if TEST_FAILS_ON_WINDOWS // This test fails on Windows because the character spacing is not applied correctly.
- [Test]
- [Category(UITestCategories.RadioButton)]
+ [Test, Order(10)]
public void RadioButton_SetContentAndCharacterSpacing_VerifyVisualState()
{
App.WaitForElement("Options");
@@ -192,8 +180,7 @@ public void RadioButton_SetContentAndCharacterSpacing_VerifyVisualState()
}
#endif
- [Test]
- [Category(UITestCategories.RadioButton)]
+ [Test, Order(11)]
public void RadioButton_SetContentAndFontSize_VerifyVisualState()
{
App.WaitForElement("Options");
@@ -209,13 +196,10 @@ public void RadioButton_SetContentAndFontSize_VerifyVisualState()
App.WaitForElement("Apply");
App.Tap("Apply");
App.WaitForElementTillPageNavigationSettled("RadioButtonControlOne");
- App.WaitForElement("SelectedValueLabelOne");
- App.Tap("SelectedValueLabelOne");
VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
}
- [Test]
- [Category(UITestCategories.RadioButton)]
+ [Test, Order(12)]
public void RadioButton_SetContentAndFontAttributes_VerifyVisualState()
{
App.WaitForElement("Options");
@@ -232,9 +216,7 @@ public void RadioButton_SetContentAndFontAttributes_VerifyVisualState()
VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
}
-#if TEST_FAILS_ON_ANDROID && TEST_FAILS_ON_WINDOWS // This test fails on Android and Windows because the text transform is not applied correctly. Issue Link - https://github.com/dotnet/maui/issues/29729
- [Test]
- [Category(UITestCategories.RadioButton)]
+ [Test, Order(13)]
public void RadioButton_SetContentAndTextTransform()
{
App.WaitForElement("Options");
@@ -249,10 +231,8 @@ public void RadioButton_SetContentAndTextTransform()
App.Tap("Apply");
VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
}
-#endif
- [Test]
- [Category(UITestCategories.RadioButton)]
+ [Test, Order(14)]
public void RadioButton_SetFontFamilyAndFontAttributes_VerifyVisualState()
{
App.WaitForElement("Options");
@@ -267,8 +247,7 @@ public void RadioButton_SetFontFamilyAndFontAttributes_VerifyVisualState()
VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
}
- [Test]
- [Category(UITestCategories.RadioButton)]
+ [Test, Order(15)]
public void RadioButton_SetFontSizeAndFontAttributes_VerifyVisualState()
{
App.WaitForElement("Options");
@@ -285,8 +264,7 @@ public void RadioButton_SetFontSizeAndFontAttributes_VerifyVisualState()
VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
}
- [Test]
- [Category(UITestCategories.RadioButton)]
+ [Test, Order(16)]
public void RadioButton_IsVisibleAndContent_VerifyVisualState()
{
App.WaitForElement("Options");
@@ -301,8 +279,8 @@ public void RadioButton_IsVisibleAndContent_VerifyVisualState()
App.Tap("Apply");
App.WaitForNoElement("RadioButtonControlOne");
}
- [Test]
- [Category(UITestCategories.RadioButton)]
+
+ [Test, Order(17)]
public void RadioButton_IsEnabledAndContent_VerifyVisualState()
{
App.WaitForElement("Options");
@@ -333,8 +311,7 @@ public void RadioButton_IsEnabledAndContent_VerifyVisualState()
Assert.That(App.WaitForElement("SelectedValueLabelOne").GetText(), Is.EqualTo(string.Empty));
}
- [Test]
- [Category(UITestCategories.RadioButton)]
+ [Test, Order(18)]
public void RadioButton_FlowDirectionAndContent_VerifyVisualState()
{
App.WaitForElement("Options");
@@ -351,8 +328,7 @@ public void RadioButton_FlowDirectionAndContent_VerifyVisualState()
VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
}
- [Test]
- [Category(UITestCategories.RadioButton)]
+ [Test, Order(19)]
public void RadioButton_SetGroupAndContent_VerifyVisualState()
{
App.WaitForElement("Options");
@@ -376,8 +352,7 @@ public void RadioButton_SetGroupAndContent_VerifyVisualState()
Assert.That(App.WaitForElement("SelectedValueLabelTwo").GetText(), Is.EqualTo("Important Only"));
}
- [Test]
- [Category(UITestCategories.RadioButton)]
+ [Test, Order(20)]
public void RadioButton_SetSelectedValueAndContent()
{
App.WaitForElement("Options");
@@ -389,4 +364,50 @@ public void RadioButton_SetSelectedValueAndContent()
App.WaitForElementTillPageNavigationSettled("RadioButtonControlOne");
Assert.That(App.WaitForElement("SelectedValueLabelOne").GetText(), Is.EqualTo("Light Mode"));
}
-}
\ No newline at end of file
+
+ [Test, Order(21)]
+ public void RadioButton_SetSelectedValueToThree_VerifyRadioButtonChecked()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("SelectedValueRadioButtonThree");
+ App.Tap("SelectedValueRadioButtonThree");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElementTillPageNavigationSettled("RadioButtonControlThree");
+ Assert.That(App.WaitForElement("SelectedValueLabelOne").GetText(), Is.EqualTo("System Default"));
+ }
+
+ [Test, Order(22)]
+ public void RadioButton_CheckedChanged_EventFires_OnSelection()
+ {
+ App.WaitForElement("RadioButtonControlOne");
+ App.Tap("RadioButtonControlOne");
+ Assert.That(App.WaitForElement("SelectedValueLabelOne").GetText(), Is.EqualTo("Dark Mode"));
+
+ App.WaitForElement("RadioButtonControlTwo");
+ App.Tap("RadioButtonControlTwo");
+ Assert.That(App.WaitForElement("SelectedValueLabelOne").GetText(), Is.EqualTo("Light Mode"));
+
+ App.WaitForElement("RadioButtonControlThree");
+ App.Tap("RadioButtonControlThree");
+ Assert.That(App.WaitForElement("SelectedValueLabelOne").GetText(), Is.EqualTo("System Default"));
+ }
+
+ [Test, Order(23)]
+ public void RadioButton_SetFontAutoScalingEnabled_VerifyVisualState()
+ {
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("FontAutoScalingEnabledFalseRadio");
+ App.Tap("FontAutoScalingEnabledFalseRadio");
+ App.WaitForElement("FontSizeEntry");
+ App.ClearText("FontSizeEntry");
+ App.EnterText("FontSizeEntry", "20");
+ App.PressEnter();
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElementTillPageNavigationSettled("RadioButtonControlOne");
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
+ }
+}
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/SafeArea_ContentViewFeatureTests.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/SafeArea_ContentViewFeatureTests.cs
new file mode 100644
index 000000000000..be73de78a111
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/SafeArea_ContentViewFeatureTests.cs
@@ -0,0 +1,2016 @@
+#if ANDROID || IOS
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests
+{
+ [Category(UITestCategories.SafeAreaEdges)]
+ public class SafeArea_ContentViewFeatureTests : _GalleryUITest
+ {
+ public const string SafeAreaFeatureMatrix = "SafeArea Feature Matrix";
+ public override string GalleryPageName => SafeAreaFeatureMatrix;
+
+ public SafeArea_ContentViewFeatureTests(TestDevice device)
+ : base(device)
+ {
+ }
+
+ ///
+ /// Reads and parses safe area inset values from the SafeAreaInsetsLabel.
+ /// Reuses the same platform-specific approach as Issue28986_SafeAreaBorderOrientation.
+ /// Format: "L:{left},T:{top},R:{right},B:{bottom},KH:{keyboardHeight},CoL:{cutoutLeft},CoR:{cutoutRight}"
+ ///
+ private (int Left, int Top, int Right, int Bottom, int KeyboardHeight, int CutoutL, int CutoutR) GetSafeAreaInsets()
+ {
+ var text = App.WaitForElement("SafeAreaInsetsLabel").GetText() ?? string.Empty;
+ var match = System.Text.RegularExpressions.Regex.Match(text, @"L:(\d+),T:(\d+),R:(\d+),B:(\d+),KH:(\d+),CoL:(\d+),CoR:(\d+)");
+ if (!match.Success)
+ throw new InvalidOperationException($"Failed to parse safe area insets from: '{text}'");
+ return (
+ int.Parse(match.Groups[1].Value),
+ int.Parse(match.Groups[2].Value),
+ int.Parse(match.Groups[3].Value),
+ int.Parse(match.Groups[4].Value),
+ int.Parse(match.Groups[5].Value),
+ int.Parse(match.Groups[6].Value),
+ int.Parse(match.Groups[7].Value)
+ );
+ }
+
+ private int GetKeyboardY()
+ {
+#if IOS
+ if (App is AppiumIOSApp iosApp && HelperExtensions.IsIOS26OrHigher(iosApp))
+ {
+ var rect = App.WaitForElement("Toolbar").GetRect();
+ return rect.Y;
+ }
+ else
+ {
+ var rect = App.WaitForElement("Done").GetRect();
+ return rect.Y;
+ }
+#elif ANDROID
+ // Calculate keyboard top Y position
+ var (_, screenHeight) = GetScreenSize();
+ var insets = GetSafeAreaInsets();
+ return screenHeight - insets.KeyboardHeight;
+#endif
+ }
+
+ public void ClickContentViewSafeAreaButton()
+ {
+ var isButtonPresent = App.FindElement("ContentViewSafeAreaButton");
+ if (isButtonPresent != null)
+ {
+ App.WaitForElement("ContentViewSafeAreaButton");
+ App.Tap("ContentViewSafeAreaButton");
+ }
+ }
+
+ private (int Width, int Height) GetScreenSize()
+ {
+ var size = ((AppiumApp)App).Driver.Manage().Window.Size;
+ return (size.Width, size.Height);
+ }
+
+ private int GetLandscapeRightInset(int right, int cutoutR)
+ {
+#if ANDROID
+ return cutoutR;
+#else
+ return right;
+#endif
+ }
+
+ // ──────────────────────────────────────────────
+ // Uniform SafeAreaRegions via Buttons
+ // ──────────────────────────────────────────────
+
+ [Test, Order(1)]
+ [Description("Content extends edge-to-edge behind system bars/notch")]
+ public void Validate_ContentView_SafeAreaEdges_None()
+ {
+ ClickContentViewSafeAreaButton();
+
+ App.WaitForElement("SafeAreaNoneButton");
+ App.Tap("SafeAreaNoneButton");
+ Assert.That(App.FindElement("SafeAreaEdgesValueLabel").GetText(), Is.EqualTo("None"));
+
+ // Portrait: top label Y should be ≈ 0 (edge-to-edge, no safe area applied)
+ var topLabelRect = App.WaitForElement("TopEdgeIndicator").GetRect();
+ Assert.That(topLabelRect.Y, Is.EqualTo(0),
+ $"None: top label Y ({topLabelRect.Y}) should be = 0 (edge-to-edge), safe area top inset is ignored");
+
+ var (_, screenHeight) = GetScreenSize();
+
+ // Portrait: bottom label bottom edge should be ≈ screenHeight (edge-to-edge, no safe area applied)
+ var bottomLabelRect = App.WaitForElement("BottomEdgeIndicator").GetRect();
+ Assert.That(Math.Abs(bottomLabelRect.Bottom), Is.EqualTo(screenHeight),
+ $"None: bottom label Y ({bottomLabelRect.Bottom}) should be ≈ screenHeight ({screenHeight})");
+ }
+
+ [Test, Order(2)]
+ [Description("Content inset from all system UI (status bar, nav bar, notch, home indicator)")]
+ public void Validate_ContentView_SafeAreaEdges_All()
+ {
+ ClickContentViewSafeAreaButton();
+
+ App.Tap("SafeAreaAllButton");
+ Assert.That(App.FindElement("SafeAreaEdgesValueLabel").GetText(), Is.EqualTo("All"));
+
+ var insets = GetSafeAreaInsets();
+ var (_, screenHeight) = GetScreenSize();
+
+ // Portrait: top label Y should be ≈ insets.Top (safe area applied)
+ var topLabelRect = App.WaitForElement("TopEdgeIndicator").GetRect();
+ Assert.That(Math.Abs(topLabelRect.Y), Is.EqualTo(insets.Top),
+ $"All: top label Y ({topLabelRect.Y}) should be equal to insets.Top ({insets.Top})");
+
+ // Portrait: bottom label bottom edge should be ≈ screenBottom - insets.Bottom
+ var bottomLabelRect = App.WaitForElement("BottomEdgeIndicator").GetRect();
+ Assert.That(Math.Abs(bottomLabelRect.Bottom), Is.EqualTo(screenHeight - insets.Bottom),
+ $"All: bottom label Y ({bottomLabelRect.Bottom}) should be equal to (screenHeight - insets.Bottom) ({screenHeight - insets.Bottom})");
+ }
+
+ [Test, Order(3)]
+ [Description("Content avoids system bars/notch but can extend under keyboard area")]
+ public void Validate_ContentView_SafeAreaEdges_Container()
+ {
+ ClickContentViewSafeAreaButton();
+
+ App.Tap("SafeAreaContainerButton");
+ Assert.That(App.FindElement("SafeAreaEdgesValueLabel").GetText(), Is.EqualTo("Container"));
+
+ var insets = GetSafeAreaInsets();
+ var (_, screenHeight) = GetScreenSize();
+
+ // Portrait: top label Y should be ≈ insets.Top (safe area applied)
+ var topLabelRect = App.WaitForElement("TopEdgeIndicator").GetRect();
+ Assert.That(Math.Abs(topLabelRect.Y), Is.EqualTo(insets.Top),
+ $"Container: top label Y ({topLabelRect.Y}) should be equal to insets.Top ({insets.Top})");
+
+ // Portrait: bottom label bottom edge should be ≈ screenBottom - insets.Bottom
+ var bottomLabelRect = App.WaitForElement("BottomEdgeIndicator").GetRect();
+ Assert.That(Math.Abs(bottomLabelRect.Bottom), Is.EqualTo(screenHeight - insets.Bottom),
+ $"Container: bottom label Y ({bottomLabelRect.Bottom}) should be equal to (screenHeight - insets.Bottom) ({screenHeight - insets.Bottom})");
+ }
+
+ [Test, Order(4)]
+ [Description("SoftInput respects safe area on top/sides but bottom is edge-to-edge without keyboard")]
+ public void Validate_ContentView_SafeAreaEdges_SoftInput()
+ {
+ ClickContentViewSafeAreaButton();
+
+ App.WaitForElement("SafeAreaSoftInputButton");
+ App.Tap("SafeAreaSoftInputButton");
+
+ Assert.That(App.FindElement("SafeAreaEdgesValueLabel").GetText(), Is.EqualTo("SoftInput"));
+
+ var insets = GetSafeAreaInsets();
+ var (_, screenHeight) = GetScreenSize();
+
+ // Portrait: top label Y should be ≈ insets.Top (SoftInput respects notch/safe area)
+ var topLabelRect = App.WaitForElement("TopEdgeIndicator").GetRect();
+ Assert.That(Math.Abs(topLabelRect.Y), Is.EqualTo(insets.Top),
+ $"SoftInput: top label Y ({topLabelRect.Y}) should be equal to insets.Top ({insets.Top})");
+
+ // Portrait: bottom label bottom edge should be ≈ screenHeight (edge-to-edge, no safe area applied)
+ var bottomLabelRect = App.WaitForElement("BottomEdgeIndicator").GetRect();
+ Assert.That(Math.Abs(bottomLabelRect.Bottom), Is.EqualTo(screenHeight),
+ $"SoftInput: bottom label Y ({bottomLabelRect.Bottom}) should be equal to screenHeight ({screenHeight})");
+ }
+
+ [Test, Order(5)]
+ [Description("Default on ContentView resolves to None — content extends edge-to-edge")]
+ public void Validate_ContentView_SafeAreaEdges_Default()
+ {
+ ClickContentViewSafeAreaButton();
+
+ App.WaitForElement("SafeAreaDefaultButton");
+ App.Tap("SafeAreaDefaultButton");
+
+ Assert.That(App.FindElement("SafeAreaEdgesValueLabel").GetText(), Is.EqualTo("Default"));
+
+ var (_, screenHeight) = GetScreenSize();
+
+ // Portrait: top label Y should be ≈ 0 (edge-to-edge, Default on ContentView = None)
+ var topLabelRect = App.WaitForElement("TopEdgeIndicator").GetRect();
+ Assert.That(topLabelRect.Y, Is.EqualTo(0),
+ $"Default: top label Y ({topLabelRect.Y}) should be = 0 (edge-to-edge), Default on ContentView resolves to None");
+
+ // Portrait: bottom label bottom edge should be ≈ screenHeight (edge-to-edge)
+ var bottomLabelRect = App.WaitForElement("BottomEdgeIndicator").GetRect();
+ Assert.That(Math.Abs(bottomLabelRect.Bottom), Is.EqualTo(screenHeight),
+ $"Default: bottom label Y ({bottomLabelRect.Bottom}) should be = screenHeight ({screenHeight}), Default on ContentView resolves to None");
+ }
+
+ // ──────────────────────────────────────────────
+ // Per-Edge Configuration (via Options)
+ // ──────────────────────────────────────────────
+
+ [Test, Order(6)]
+ [Description("Only top avoids status bar/notch. Bottom edge-to-edge.")]
+ public void Validate_ContentView_PerEdge_TopContainerOnly()
+ {
+ ClickContentViewSafeAreaButton();
+
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("TopContainer");
+ App.Tap("TopContainer");
+ App.Tap("BottomNone");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+
+ App.WaitForElement("SafeAreaEdgesValueLabel");
+ Assert.That(App.FindElement("SafeAreaEdgesValueLabel").GetText(), Is.EqualTo("L:None, T:Container, R:None, B:None"));
+
+ var insets = GetSafeAreaInsets();
+ var (_, screenHeight) = GetScreenSize();
+
+ // Portrait: Container — should be inset by safe area top
+ var topLabelRect = App.WaitForElement("TopEdgeIndicator").GetRect();
+ Assert.That(Math.Abs(topLabelRect.Y), Is.EqualTo(insets.Top),
+ $"Top (Container): label Y ({topLabelRect.Y}) should be ≈ insets.Top ({insets.Top})");
+
+ // Portrait: bottom label bottom edge should be ≈ screenHeight (edge-to-edge, no safe area applied)
+ var bottomLabelRect = App.WaitForElement("BottomEdgeIndicator").GetRect();
+ Assert.That(Math.Abs(bottomLabelRect.Bottom), Is.EqualTo(screenHeight),
+ $"None: bottom label Y ({bottomLabelRect.Bottom}) should be ≈ screenHeight ({screenHeight})");
+ }
+
+ [Test, Order(7)]
+ [Description("Top avoids system bars; bottom avoids only keyboard")]
+ public void Validate_ContentView_PerEdge_BottomSoftInput_TopContainer()
+ {
+ ClickContentViewSafeAreaButton();
+
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("TopContainer");
+ App.Tap("TopContainer");
+ App.Tap("BottomSoftInput");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+
+ App.WaitForElement("SafeAreaEdgesValueLabel");
+ Assert.That(App.FindElement("SafeAreaEdgesValueLabel").GetText(), Is.EqualTo("L:None, T:Container, R:None, B:SoftInput"));
+
+ var insets = GetSafeAreaInsets();
+ var (_, screenHeight) = GetScreenSize();
+
+ // Portrait: only validate top and bottom — no left/right safe area insets in portrait
+ // Top: Container — should be inset by safe area top
+ var topLabelRect = App.WaitForElement("TopEdgeIndicator").GetRect();
+ Assert.That(Math.Abs(topLabelRect.Y), Is.EqualTo(insets.Top),
+ $"Top (Container): label Y ({topLabelRect.Y}) should be ≈ insets.Top ({insets.Top})");
+
+ // Portrait: bottom label bottom edge should be ≈ screenHeight (edge-to-edge, no safe area applied)
+ var bottomLabelRect = App.WaitForElement("BottomEdgeIndicator").GetRect();
+ Assert.That(Math.Abs(bottomLabelRect.Bottom), Is.EqualTo(screenHeight),
+ $"SoftInput: bottom label Y ({bottomLabelRect.Bottom}) should be ≈ screenHeight ({screenHeight})");
+ }
+
+ [Test, Order(8)]
+ [Description("Top/bottom respect all insets")]
+ public void Validate_ContentView_PerEdge_TopBottomAll_SidesNone()
+ {
+ ClickContentViewSafeAreaButton();
+
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("TopAll");
+ App.Tap("TopAll");
+ App.Tap("BottomAll");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+
+ App.WaitForElement("SafeAreaEdgesValueLabel");
+ Assert.That(App.FindElement("SafeAreaEdgesValueLabel").GetText(), Is.EqualTo("L:None, T:All, R:None, B:All"));
+
+ var insets = GetSafeAreaInsets();
+ var (_, screenHeight) = GetScreenSize();
+
+ // Portrait: top label Y should be ≈ insets.Top (All applies safe area on top)
+ var topLabelRect = App.WaitForElement("TopEdgeIndicator").GetRect();
+ Assert.That(Math.Abs(topLabelRect.Y), Is.EqualTo(insets.Top),
+ $"All: top label Y ({topLabelRect.Y}) should be = insets.Top ({insets.Top})");
+
+ // Portrait: bottom label bottom edge should be ≈ screenBottom - insets.Bottom (All applies safe area on bottom)
+ var bottomLabelRect = App.WaitForElement("BottomEdgeIndicator").GetRect();
+ Assert.That(Math.Abs(bottomLabelRect.Bottom), Is.EqualTo(screenHeight - insets.Bottom),
+ $"All: bottom label Y ({bottomLabelRect.Bottom}) should be = (screenHeight - insets.Bottom) ({screenHeight - insets.Bottom})");
+ }
+
+ [Test, Order(9)]
+ [Description("Each edge independently applies its behavior")]
+ public void Validate_ContentView_PerEdge_AllDifferent()
+ {
+ ClickContentViewSafeAreaButton();
+
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("TopContainer");
+ App.Tap("TopContainer");
+ App.Tap("BottomAll");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+
+ App.WaitForElement("SafeAreaEdgesValueLabel");
+ Assert.That(App.FindElement("SafeAreaEdgesValueLabel").GetText(), Is.EqualTo("L:None, T:Container, R:None, B:All"));
+
+ var insets = GetSafeAreaInsets();
+ var (_, screenHeight) = GetScreenSize();
+
+ // Portrait: top label Y should be ≈ insets.Top (safe area applied)
+ var topLabelRect = App.WaitForElement("TopEdgeIndicator").GetRect();
+ Assert.That(Math.Abs(topLabelRect.Y), Is.EqualTo(insets.Top),
+ $"Container: top label Y ({topLabelRect.Y}) should be equal to insets.Top ({insets.Top})");
+
+ // Portrait: bottom label bottom edge should be ≈ screenBottom - insets.Bottom
+ var bottomLabelRect = App.WaitForElement("BottomEdgeIndicator").GetRect();
+ Assert.That(Math.Abs(bottomLabelRect.Bottom), Is.EqualTo(screenHeight - insets.Bottom),
+ $"All: bottom label Y ({bottomLabelRect.Bottom}) should be equal to (screenHeight - insets.Bottom) ({screenHeight - insets.Bottom})");
+ }
+
+ // ──────────────────────────────────────────────
+ // Keyboard Interaction (SoftInput)
+ // ──────────────────────────────────────────────
+
+ [Test, Order(10)]
+ [Description("None → All → keyboard open → Container → dismiss → All: positions correct at each step")]
+ public void Validate_ContentView_SafeArea_NoneThenAllKeyboardContainerDismissAll()
+ {
+ ClickContentViewSafeAreaButton();
+ App.DismissKeyboard();
+
+ var insets = GetSafeAreaInsets();
+ var (_, screenHeight) = GetScreenSize();
+
+ // ── Step 1: Click None and verify ──
+ App.WaitForElement("SafeAreaNoneButton");
+ App.Tap("SafeAreaNoneButton");
+ Assert.That(App.FindElement("SafeAreaEdgesValueLabel").GetText(), Is.EqualTo("None"));
+
+ var topLabelRect = App.WaitForElement("TopEdgeIndicator").GetRect();
+ Assert.That(topLabelRect.Y, Is.EqualTo(0),
+ $"None: top label Y ({topLabelRect.Y}) should be 0 (edge-to-edge)");
+
+ var bottomLabelRect = App.WaitForElement("BottomEdgeIndicator").GetRect();
+ Assert.That(Math.Abs(bottomLabelRect.Bottom), Is.EqualTo(screenHeight),
+ $"None: bottom label Bottom ({bottomLabelRect.Bottom}) should be equal to screenHeight ({screenHeight})");
+
+ // ── Step 2: Click All and verify ──
+ App.Tap("SafeAreaAllButton");
+ Assert.That(App.FindElement("SafeAreaEdgesValueLabel").GetText(), Is.EqualTo("All"));
+
+ topLabelRect = App.WaitForElement("TopEdgeIndicator").GetRect();
+ Assert.That(Math.Abs(topLabelRect.Y), Is.EqualTo(insets.Top),
+ $"All: top label Y ({topLabelRect.Y}) should be equal to insets.Top ({insets.Top})");
+
+ bottomLabelRect = App.WaitForElement("BottomEdgeIndicator").GetRect();
+ Assert.That(Math.Abs(bottomLabelRect.Bottom), Is.EqualTo(screenHeight - insets.Bottom),
+ $"All: bottom label Bottom ({bottomLabelRect.Bottom}) should be equal to (screenHeight - insets.Bottom) ({screenHeight - insets.Bottom})");
+
+ // ── Step 3: Open keyboard and verify (All adjusts for keyboard) ──
+ Assert.That(App.IsKeyboardShown(), Is.False, "Keyboard should not be visible before tapping entry");
+ App.Tap("SafeAreaTestEntry");
+ App.WaitForKeyboardToShow();
+ Assert.That(App.IsKeyboardShown(), Is.True, "Keyboard should be visible after tapping entry");
+
+ var keyboardY = GetKeyboardY();
+
+ topLabelRect = App.WaitForElement("TopEdgeIndicator").GetRect();
+ Assert.That(Math.Abs(topLabelRect.Y), Is.EqualTo(insets.Top),
+ $"All (keyboard open): top label Y ({topLabelRect.Y}) should be equal to insets.Top ({insets.Top})");
+
+ bottomLabelRect = App.WaitForElement("BottomEdgeIndicator").GetRect();
+ Assert.That(bottomLabelRect.Bottom, Is.EqualTo(keyboardY),
+ $"All (keyboard open): bottom label Bottom ({bottomLabelRect.Bottom}) should equal keyboard Y ({keyboardY})");
+
+ // ── Step 4: Switch to Container while keyboard is open ──
+ App.Tap("SafeAreaContainerButton");
+ Assert.That(App.FindElement("SafeAreaEdgesValueLabel").GetText(), Is.EqualTo("Container"));
+
+ topLabelRect = App.WaitForElement("TopEdgeIndicator").GetRect();
+ Assert.That(Math.Abs(topLabelRect.Y), Is.EqualTo(insets.Top),
+ $"Container (keyboard open): top label Y ({topLabelRect.Y}) should be equal to insets.Top ({insets.Top})");
+
+#if !ANDROID // On Android, Appium does not find the bottom label when the keyboard is open
+ bottomLabelRect = App.WaitForElement("BottomEdgeIndicator").GetRect();
+ Assert.That(Math.Abs(bottomLabelRect.Bottom), Is.EqualTo(screenHeight - insets.Bottom),
+ $"Container (keyboard open): bottom label Bottom ({bottomLabelRect.Bottom}) should be equal to (screenHeight - insets.Bottom) ({screenHeight - insets.Bottom})");
+#endif
+ // ── Step 5: Dismiss keyboard ──
+ App.DismissKeyboard();
+ App.WaitForKeyboardToHide();
+ Assert.That(App.IsKeyboardShown(), Is.False, "Keyboard should be hidden after dismissal");
+
+ // ── Step 6: Click All and verify ──
+ App.Tap("SafeAreaAllButton");
+ Assert.That(App.FindElement("SafeAreaEdgesValueLabel").GetText(), Is.EqualTo("All"));
+
+ topLabelRect = App.WaitForElement("TopEdgeIndicator").GetRect();
+ Assert.That(Math.Abs(topLabelRect.Y), Is.EqualTo(insets.Top),
+ $"All (after dismiss): top label Y ({topLabelRect.Y}) should be equal to insets.Top ({insets.Top})");
+
+ bottomLabelRect = App.WaitForElement("BottomEdgeIndicator").GetRect();
+ Assert.That(Math.Abs(bottomLabelRect.Bottom), Is.EqualTo(screenHeight - insets.Bottom),
+ $"All (after dismiss): bottom label Bottom ({bottomLabelRect.Bottom}) should be equal to (screenHeight - insets.Bottom) ({screenHeight - insets.Bottom})");
+ }
+
+ // ──────────────────────────────────────────────
+ // Keyboard Position Validation
+ // ──────────────────────────────────────────────
+ // Validates that the bottom indicator moves up when keyboard is shown with modes that
+ // adjust for keyboard (All/SoftInput), and does NOT move with modes that don't (None/Container).
+
+ [Test, Order(11)]
+ [Description("With All, bottom indicator moves up when keyboard is shown and restores when dismissed")]
+ public void Validate_ContentView_Keyboard_All_BottomMovesUp()
+ {
+ ClickContentViewSafeAreaButton();
+ App.DismissKeyboard();
+
+ App.WaitForElement("SafeAreaAllButton");
+ App.Tap("SafeAreaAllButton");
+
+ Assert.That(App.FindElement("SafeAreaEdgesValueLabel").GetText(), Is.EqualTo("All"));
+
+ var insets = GetSafeAreaInsets();
+ var (_, screenHeight) = GetScreenSize();
+
+ // ── Before keyboard ──
+ var topLabelBeforeRect = App.WaitForElement("TopEdgeIndicator").GetRect();
+ Assert.That(Math.Abs(topLabelBeforeRect.Y), Is.EqualTo(insets.Top),
+ $"Before keyboard - top label Y ({topLabelBeforeRect.Y}) should be equal to insets.Top ({insets.Top})");
+
+ var bottomLabelBeforeRect = App.WaitForElement("BottomEdgeIndicator").GetRect();
+ Assert.That(Math.Abs(bottomLabelBeforeRect.Bottom), Is.EqualTo(screenHeight - insets.Bottom),
+ $"Before keyboard - bottom label Bottom ({bottomLabelBeforeRect.Bottom}) should be equal to (screenHeight - insets.Bottom) ({screenHeight - insets.Bottom})");
+
+ // ── Show keyboard ──
+ Assert.That(App.IsKeyboardShown(), Is.False, "Keyboard should not be visible before tapping entry");
+ App.Tap("SafeAreaTestEntry");
+ App.WaitForKeyboardToShow();
+ Assert.That(App.IsKeyboardShown(), Is.True, "Keyboard should be visible after tapping entry");
+
+ var keyboardY = GetKeyboardY();
+
+ // Bottom should have moved up to the keyboard top
+ var bottomLabelDuringRect = App.WaitForElement("BottomEdgeIndicator").GetRect();
+ Assert.That(bottomLabelDuringRect.Bottom, Is.EqualTo(keyboardY),
+ $"During keyboard - bottom label Bottom ({bottomLabelDuringRect.Bottom}) should equal keyboard Y ({keyboardY})");
+
+ // Top should remain unchanged
+ var topLabelDuringRect = App.WaitForElement("TopEdgeIndicator").GetRect();
+ Assert.That(topLabelDuringRect.Y, Is.EqualTo(topLabelBeforeRect.Y),
+ $"During keyboard - top label Y ({topLabelDuringRect.Y}) should remain at ({topLabelBeforeRect.Y})");
+
+ // ── Dismiss keyboard ──
+ App.DismissKeyboard();
+ App.WaitForKeyboardToHide();
+ Assert.That(App.IsKeyboardShown(), Is.False, "Keyboard should be hidden after dismissal");
+
+ // Top should return to its original position
+ var topLabelAfterRect = App.WaitForElement("TopEdgeIndicator").GetRect();
+ Assert.That(topLabelAfterRect.Y, Is.EqualTo(topLabelBeforeRect.Y),
+ $"After keyboard - top label Y ({topLabelAfterRect.Y}) should return to original ({topLabelBeforeRect.Y})");
+
+ // Bottom should return to its original position
+ var bottomLabelAfterRect = App.WaitForElement("BottomEdgeIndicator").GetRect();
+ Assert.That(bottomLabelAfterRect.Bottom, Is.EqualTo(bottomLabelBeforeRect.Bottom),
+ $"After keyboard - bottom label Bottom ({bottomLabelAfterRect.Bottom}) should return to original ({bottomLabelBeforeRect.Bottom})");
+ }
+
+ [Test, Order(12)]
+ [Description("With SoftInput, bottom indicator moves up when keyboard is shown and restores when dismissed")]
+ public void Validate_ContentView_Keyboard_SoftInput_BottomMovesUp()
+ {
+ ClickContentViewSafeAreaButton();
+ App.DismissKeyboard();
+
+ App.WaitForElement("SafeAreaSoftInputButton");
+ App.Tap("SafeAreaSoftInputButton");
+
+ Assert.That(App.FindElement("SafeAreaEdgesValueLabel").GetText(), Is.EqualTo("SoftInput"));
+
+ var insets = GetSafeAreaInsets();
+ var (_, screenHeight) = GetScreenSize();
+
+ // ── Before keyboard ──
+ var topLabelBeforeRect = App.WaitForElement("TopEdgeIndicator").GetRect();
+ Assert.That(Math.Abs(topLabelBeforeRect.Y), Is.EqualTo(insets.Top),
+ $"Before keyboard - top label Y ({topLabelBeforeRect.Y}) should be equal to insets.Top ({insets.Top})");
+
+ var bottomLabelBeforeRect = App.WaitForElement("BottomEdgeIndicator").GetRect();
+ Assert.That(Math.Abs(bottomLabelBeforeRect.Bottom), Is.EqualTo(screenHeight),
+ $"Before keyboard - bottom label Bottom ({bottomLabelBeforeRect.Bottom}) should be equal to screenHeight ({screenHeight})");
+
+ // ── Show keyboard ──
+ Assert.That(App.IsKeyboardShown(), Is.False, "Keyboard should not be visible before tapping entry");
+ App.Tap("SafeAreaTestEntry");
+ App.WaitForKeyboardToShow();
+ Assert.That(App.IsKeyboardShown(), Is.True, "Keyboard should be visible after tapping entry");
+
+ var keyboardY = GetKeyboardY();
+
+ // Bottom should have moved up to the keyboard top
+ var bottomLabelDuringRect = App.WaitForElement("BottomEdgeIndicator").GetRect();
+ Assert.That(bottomLabelDuringRect.Bottom, Is.EqualTo(keyboardY),
+ $"During keyboard - bottom label Bottom ({bottomLabelDuringRect.Bottom}) should equal keyboard Y ({keyboardY})");
+
+ // Top should remain unchanged
+ var topLabelDuringRect = App.WaitForElement("TopEdgeIndicator").GetRect();
+ Assert.That(topLabelDuringRect.Y, Is.EqualTo(topLabelBeforeRect.Y),
+ $"During keyboard - top label Y ({topLabelDuringRect.Y}) should remain at ({topLabelBeforeRect.Y})");
+
+ // ── Dismiss keyboard ──
+ App.DismissKeyboard();
+ App.WaitForKeyboardToHide();
+ Assert.That(App.IsKeyboardShown(), Is.False, "Keyboard should be hidden after dismissal");
+
+ // Top should return to its original position
+ var topLabelAfterRect = App.WaitForElement("TopEdgeIndicator").GetRect();
+ Assert.That(topLabelAfterRect.Y, Is.EqualTo(topLabelBeforeRect.Y),
+ $"After keyboard - top label Y ({topLabelAfterRect.Y}) should return to original ({topLabelBeforeRect.Y})");
+
+ // Bottom should return to its original position
+ var bottomLabelAfterRect = App.WaitForElement("BottomEdgeIndicator").GetRect();
+ Assert.That(bottomLabelAfterRect.Bottom, Is.EqualTo(bottomLabelBeforeRect.Bottom),
+ $"After keyboard - bottom label Bottom ({bottomLabelAfterRect.Bottom}) should return to original ({bottomLabelBeforeRect.Bottom})");
+ }
+
+ [Test, Order(13)]
+ [Description("With None, bottom indicator does NOT move when keyboard is shown")]
+ public void Validate_ContentView_Keyboard_None_BottomStays()
+ {
+ ClickContentViewSafeAreaButton();
+ App.DismissKeyboard();
+
+ App.WaitForElement("SafeAreaNoneButton");
+ App.Tap("SafeAreaNoneButton");
+
+ var (_, screenHeight) = GetScreenSize();
+
+ // ── Before keyboard ──
+ var topLabelBeforeRect = App.WaitForElement("TopEdgeIndicator").GetRect();
+ Assert.That(topLabelBeforeRect.Y, Is.EqualTo(0),
+ $"Before keyboard - top label Y ({topLabelBeforeRect.Y}) should be 0 (edge-to-edge)");
+
+ var bottomLabelBeforeRect = App.WaitForElement("BottomEdgeIndicator").GetRect();
+ Assert.That(Math.Abs(bottomLabelBeforeRect.Bottom), Is.EqualTo(screenHeight),
+ $"Before keyboard - bottom label Bottom ({bottomLabelBeforeRect.Bottom}) should be equal to screenHeight ({screenHeight})");
+
+ // ── Show keyboard ──
+ Assert.That(App.IsKeyboardShown(), Is.False, "Keyboard should not be visible before tapping entry");
+ App.Tap("SafeAreaTestEntry");
+ App.WaitForKeyboardToShow();
+ Assert.That(App.IsKeyboardShown(), Is.True, "Keyboard should be visible after tapping entry");
+
+ var topLabelDuringRect = App.WaitForElement("TopEdgeIndicator").GetRect();
+ Assert.That(topLabelDuringRect.Y, Is.EqualTo(0),
+ $"During keyboard - top label Y ({topLabelDuringRect.Y}) should be 0 (edge-to-edge)");
+
+#if !ANDROID // On Android, Appium does not find the bottom label when the keyboard is open
+ var bottomLabelDuringRect = App.WaitForElement("BottomEdgeIndicator").GetRect();
+ Assert.That(Math.Abs(bottomLabelDuringRect.Bottom), Is.EqualTo(screenHeight),
+ $"During keyboard - bottom label Bottom ({bottomLabelDuringRect.Bottom}) should be equal to screenHeight ({screenHeight})");
+#endif
+ App.DismissKeyboard();
+ App.WaitForKeyboardToHide();
+ Assert.That(App.IsKeyboardShown(), Is.False, "Keyboard should be hidden after dismissal");
+
+ var topLabelAfterRect = App.WaitForElement("TopEdgeIndicator").GetRect();
+ Assert.That(topLabelAfterRect.Y, Is.EqualTo(topLabelBeforeRect.Y),
+ $"After keyboard - top label Y ({topLabelAfterRect.Y}) should return to original ({topLabelBeforeRect.Y})");
+
+ var bottomLabelAfterRect = App.WaitForElement("BottomEdgeIndicator").GetRect();
+ Assert.That(bottomLabelAfterRect.Bottom, Is.EqualTo(bottomLabelBeforeRect.Bottom),
+ $"After keyboard - bottom label Bottom ({bottomLabelAfterRect.Bottom}) should return to original ({bottomLabelBeforeRect.Bottom})");
+
+ }
+
+ [Test, Order(14)]
+ [Description("With Container, bottom indicator does NOT move when keyboard is shown")]
+ public void Validate_ContentView_Keyboard_Container_BottomStays()
+ {
+ ClickContentViewSafeAreaButton();
+ App.DismissKeyboard();
+
+ App.WaitForElement("SafeAreaContainerButton");
+ App.Tap("SafeAreaContainerButton");
+
+ Assert.That(App.FindElement("SafeAreaEdgesValueLabel").GetText(), Is.EqualTo("Container"));
+
+ var insets = GetSafeAreaInsets();
+ var (_, screenHeight) = GetScreenSize();
+
+ // ── Before keyboard ──
+ var topLabelBeforeRect = App.WaitForElement("TopEdgeIndicator").GetRect();
+ Assert.That(Math.Abs(topLabelBeforeRect.Y), Is.EqualTo(insets.Top),
+ $"Before keyboard - top label Y ({topLabelBeforeRect.Y}) should be equal to insets.Top ({insets.Top})");
+
+ var bottomLabelBeforeRect = App.WaitForElement("BottomEdgeIndicator").GetRect();
+ Assert.That(Math.Abs(bottomLabelBeforeRect.Bottom), Is.EqualTo(screenHeight - insets.Bottom),
+ $"Before keyboard - bottom label Bottom ({bottomLabelBeforeRect.Bottom}) should be equal to (screenHeight - insets.Bottom) ({screenHeight - insets.Bottom})");
+
+ Assert.That(App.IsKeyboardShown(), Is.False, "Keyboard should not be visible before tapping entry");
+ App.Tap("SafeAreaTestEntry");
+ App.WaitForKeyboardToShow();
+ Assert.That(App.IsKeyboardShown(), Is.True, "Keyboard should be visible after tapping entry");
+
+#if !ANDROID // On Android, Appium does not find the bottom label when the keyboard is open
+ // Bottom should not have moved up to the keyboard top
+ var bottomLabelDuringRect = App.WaitForElement("BottomEdgeIndicator").GetRect();
+ Assert.That(bottomLabelDuringRect.Bottom, Is.EqualTo(screenHeight - insets.Bottom),
+ $"During keyboard - bottom label Bottom ({bottomLabelDuringRect.Bottom}) should equal (screenHeight - insets.Bottom) ({screenHeight - insets.Bottom})");
+#endif
+ // Top should remain unchanged
+ var topLabelDuringRect = App.WaitForElement("TopEdgeIndicator").GetRect();
+ Assert.That(topLabelDuringRect.Y, Is.EqualTo(topLabelBeforeRect.Y),
+ $"During keyboard - top label Y ({topLabelDuringRect.Y}) should remain at ({topLabelBeforeRect.Y})");
+
+ App.DismissKeyboard();
+ App.WaitForKeyboardToHide();
+ Assert.That(App.IsKeyboardShown(), Is.False, "Keyboard should be hidden after dismissal");
+
+ // Top should return to its original position
+ var topLabelAfterRect = App.WaitForElement("TopEdgeIndicator").GetRect();
+ Assert.That(topLabelAfterRect.Y, Is.EqualTo(topLabelBeforeRect.Y),
+ $"After keyboard - top label Y ({topLabelAfterRect.Y}) should return to original ({topLabelBeforeRect.Y})");
+
+ // Bottom should return to its original position
+ var bottomLabelAfterRect = App.WaitForElement("BottomEdgeIndicator").GetRect();
+ Assert.That(bottomLabelAfterRect.Bottom, Is.EqualTo(bottomLabelBeforeRect.Bottom),
+ $"After keyboard - bottom label Bottom ({bottomLabelAfterRect.Bottom}) should return to original ({bottomLabelBeforeRect.Bottom})");
+
+ }
+
+ // ──────────────────────────────────────────────
+ // Keyboard + Runtime SafeArea Changes
+ // ──────────────────────────────────────────────
+
+#if TEST_FAILS_ON_IOS // Issue Link - https://github.com/dotnet/maui/issues/34847
+
+ [Test, Order(15)]
+ [Description("Switch None to All while keyboard is open — bottom indicator moves up")]
+ public void Validate_ContentView_KeyboardRuntime_SwitchNoneToAll_WhileKeyboardOpen()
+ {
+ ClickContentViewSafeAreaButton();
+ App.DismissKeyboard();
+
+ App.WaitForElement("SafeAreaNoneButton");
+ App.Tap("SafeAreaNoneButton");
+
+ var insets = GetSafeAreaInsets();
+ var (_, screenHeight) = GetScreenSize();
+
+ // ── Before keyboard (None) ──
+ var topLabelBeforeRect = App.WaitForElement("TopEdgeIndicator").GetRect();
+ Assert.That(topLabelBeforeRect.Y, Is.EqualTo(0),
+ $"Before keyboard - top label Y ({topLabelBeforeRect.Y}) should be 0 (edge-to-edge)");
+
+ var bottomLabelBeforeRect = App.WaitForElement("BottomEdgeIndicator").GetRect();
+ Assert.That(Math.Abs(bottomLabelBeforeRect.Bottom), Is.EqualTo(screenHeight),
+ $"Before keyboard - bottom label Bottom ({bottomLabelBeforeRect.Bottom}) should be equal to screenHeight ({screenHeight})");
+
+ // ── Show keyboard (None) ──
+ Assert.That(App.IsKeyboardShown(), Is.False, "Keyboard should not be visible before tapping entry");
+ App.Tap("SafeAreaTestEntry");
+ App.WaitForKeyboardToShow();
+ Assert.That(App.IsKeyboardShown(), Is.True, "Keyboard should be visible after tapping entry");
+
+ // With None, bottom should NOT move
+ var topLabelDuringNoneRect = App.WaitForElement("TopEdgeIndicator").GetRect();
+ Assert.That(topLabelDuringNoneRect.Y, Is.EqualTo(0),
+ $"During keyboard (None) - top label Y ({topLabelDuringNoneRect.Y}) should be 0 (edge-to-edge)");
+
+#if !ANDROID // On Android, Appium does not find the bottom label when the keyboard is open
+ var bottomLabelDuringNoneRect = App.WaitForElement("BottomEdgeIndicator").GetRect();
+ Assert.That(Math.Abs(bottomLabelDuringNoneRect.Bottom), Is.EqualTo(screenHeight),
+ $"During keyboard (None) - bottom label Bottom ({bottomLabelDuringNoneRect.Bottom}) should be equal to screenHeight ({screenHeight})");
+#endif
+ // ── Switch to All while keyboard is open ──
+ App.Tap("SafeAreaAllButton");
+ Assert.That(App.FindElement("SafeAreaEdgesValueLabel").GetText(), Is.EqualTo("All"));
+
+ var keyboardY = GetKeyboardY();
+
+ // With All, bottom should move up to keyboard top
+ var topLabelDuringAllRect = App.WaitForElement("TopEdgeIndicator").GetRect();
+ Assert.That(Math.Abs(topLabelDuringAllRect.Y), Is.EqualTo(insets.Top),
+ $"During keyboard (All) - top label Y ({topLabelDuringAllRect.Y}) should be equal to insets.Top ({insets.Top})");
+
+ var bottomLabelDuringAllRect = App.WaitForElement("BottomEdgeIndicator").GetRect();
+ Assert.That(bottomLabelDuringAllRect.Bottom, Is.EqualTo(keyboardY),
+ $"During keyboard (All) - bottom label Bottom ({bottomLabelDuringAllRect.Bottom}) should equal keyboard Y ({keyboardY})");
+
+ // ── Dismiss keyboard ──
+ App.DismissKeyboard();
+ App.WaitForKeyboardToHide();
+ Assert.That(App.IsKeyboardShown(), Is.False, "Keyboard should be hidden after dismissal");
+
+ // After keyboard (All): top at insets.Top, bottom at (screenHeight - insets.Bottom)
+ var topLabelAfterRect = App.WaitForElement("TopEdgeIndicator").GetRect();
+ Assert.That(Math.Abs(topLabelAfterRect.Y), Is.EqualTo(insets.Top),
+ $"After keyboard - top label Y ({topLabelAfterRect.Y}) should be equal to insets.Top ({insets.Top})");
+
+ var bottomLabelAfterRect = App.WaitForElement("BottomEdgeIndicator").GetRect();
+ Assert.That(Math.Abs(bottomLabelAfterRect.Bottom), Is.EqualTo(screenHeight - insets.Bottom),
+ $"After keyboard - bottom label Bottom ({bottomLabelAfterRect.Bottom}) should be equal to (screenHeight - insets.Bottom) ({screenHeight - insets.Bottom})");
+ }
+
+ [Test, Order(16)]
+ [Description("Switch None to SoftInput while keyboard is open — bottom indicator moves up")]
+ public void Validate_ContentView_KeyboardRuntime_SwitchNoneToSoftInput_WhileKeyboardOpen()
+ {
+ ClickContentViewSafeAreaButton();
+ App.DismissKeyboard();
+
+ App.WaitForElement("SafeAreaNoneButton");
+ App.Tap("SafeAreaNoneButton");
+
+ var insets = GetSafeAreaInsets();
+ var (_, screenHeight) = GetScreenSize();
+
+ // ── Before keyboard (None) ──
+ var topLabelBeforeRect = App.WaitForElement("TopEdgeIndicator").GetRect();
+ Assert.That(topLabelBeforeRect.Y, Is.EqualTo(0),
+ $"Before keyboard - top label Y ({topLabelBeforeRect.Y}) should be 0 (edge-to-edge)");
+
+ var bottomLabelBeforeRect = App.WaitForElement("BottomEdgeIndicator").GetRect();
+ Assert.That(Math.Abs(bottomLabelBeforeRect.Bottom), Is.EqualTo(screenHeight),
+ $"Before keyboard - bottom label Bottom ({bottomLabelBeforeRect.Bottom}) should be equal to screenHeight ({screenHeight})");
+
+ // ── Show keyboard (None) ──
+ Assert.That(App.IsKeyboardShown(), Is.False, "Keyboard should not be visible before tapping entry");
+ App.Tap("SafeAreaTestEntry");
+ App.WaitForKeyboardToShow();
+ Assert.That(App.IsKeyboardShown(), Is.True, "Keyboard should be visible after tapping entry");
+
+ // With None, bottom should NOT move
+ var topLabelDuringNoneRect = App.WaitForElement("TopEdgeIndicator").GetRect();
+ Assert.That(topLabelDuringNoneRect.Y, Is.EqualTo(0),
+ $"During keyboard (None) - top label Y ({topLabelDuringNoneRect.Y}) should be 0 (edge-to-edge)");
+
+#if !ANDROID // On Android, Appium does not find the bottom label when the keyboard is open
+ var bottomLabelDuringNoneRect = App.WaitForElement("BottomEdgeIndicator").GetRect();
+ Assert.That(Math.Abs(bottomLabelDuringNoneRect.Bottom), Is.EqualTo(screenHeight),
+ $"During keyboard (None) - bottom label Bottom ({bottomLabelDuringNoneRect.Bottom}) should be equal to screenHeight ({screenHeight})");
+#endif
+ // ── Switch to SoftInput while keyboard is open ──
+ App.Tap("SafeAreaSoftInputButton");
+ Assert.That(App.FindElement("SafeAreaEdgesValueLabel").GetText(), Is.EqualTo("SoftInput"));
+
+ var keyboardY = GetKeyboardY();
+
+ // With SoftInput, bottom should move up to keyboard top
+ var topLabelDuringSoftInputRect = App.WaitForElement("TopEdgeIndicator").GetRect();
+ Assert.That(Math.Abs(topLabelDuringSoftInputRect.Y), Is.EqualTo(insets.Top),
+ $"During keyboard (SoftInput) - top label Y ({topLabelDuringSoftInputRect.Y}) should be equal to insets.Top ({insets.Top})");
+
+ var bottomLabelDuringSoftInputRect = App.WaitForElement("BottomEdgeIndicator").GetRect();
+ Assert.That(bottomLabelDuringSoftInputRect.Bottom, Is.EqualTo(keyboardY),
+ $"During keyboard (SoftInput) - bottom label Bottom ({bottomLabelDuringSoftInputRect.Bottom}) should equal keyboard Y ({keyboardY})");
+
+ // ── Dismiss keyboard ──
+ App.DismissKeyboard();
+ App.WaitForKeyboardToHide();
+ Assert.That(App.IsKeyboardShown(), Is.False, "Keyboard should be hidden after dismissal");
+
+ // After keyboard (SoftInput): top at insets.Top, bottom at screenHeight (SoftInput bottom is edge-to-edge without keyboard)
+ var topLabelAfterRect = App.WaitForElement("TopEdgeIndicator").GetRect();
+ Assert.That(Math.Abs(topLabelAfterRect.Y), Is.EqualTo(insets.Top),
+ $"After keyboard - top label Y ({topLabelAfterRect.Y}) should be equal to insets.Top ({insets.Top})");
+
+ var bottomLabelAfterRect = App.WaitForElement("BottomEdgeIndicator").GetRect();
+ Assert.That(Math.Abs(bottomLabelAfterRect.Bottom), Is.EqualTo(screenHeight),
+ $"After keyboard - bottom label Bottom ({bottomLabelAfterRect.Bottom}) should be equal to screenHeight ({screenHeight})");
+ }
+#endif
+
+ [Test, Order(17)]
+ [Description("Switch All to None while keyboard is open — bottom indicator drops back")]
+ public void Validate_ContentView_KeyboardRuntime_SwitchAllToNone_WhileKeyboardOpen()
+ {
+ ClickContentViewSafeAreaButton();
+ App.DismissKeyboard();
+
+ // Navigating to the Options page to reset the ViewModel to its default settings before the test to ensure consistent testing
+ App.WaitForElement("Options");
+ App.Tap("Options");
+
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+
+ App.WaitForElement("SafeAreaAllButton");
+ App.Tap("SafeAreaAllButton");
+
+ var insets = GetSafeAreaInsets();
+ var (_, screenHeight) = GetScreenSize();
+
+ // ── Before keyboard (All) ──
+ var topLabelBeforeRect = App.WaitForElement("TopEdgeIndicator").GetRect();
+ Assert.That(Math.Abs(topLabelBeforeRect.Y), Is.EqualTo(insets.Top),
+ $"Before keyboard - top label Y ({topLabelBeforeRect.Y}) should be equal to insets.Top ({insets.Top})");
+
+ var bottomLabelBeforeRect = App.WaitForElement("BottomEdgeIndicator").GetRect();
+ Assert.That(Math.Abs(bottomLabelBeforeRect.Bottom), Is.EqualTo(screenHeight - insets.Bottom),
+ $"Before keyboard - bottom label Bottom ({bottomLabelBeforeRect.Bottom}) should be equal to (screenHeight - insets.Bottom) ({screenHeight - insets.Bottom})");
+
+ // ── Show keyboard (All) ──
+ Assert.That(App.IsKeyboardShown(), Is.False, "Keyboard should not be visible before tapping entry");
+ App.Tap("SafeAreaTestEntry");
+ App.WaitForKeyboardToShow();
+ Assert.That(App.IsKeyboardShown(), Is.True, "Keyboard should be visible after tapping entry");
+
+ var keyboardY = GetKeyboardY();
+
+ // With All, bottom should move up to keyboard top
+ var topLabelDuringAllRect = App.WaitForElement("TopEdgeIndicator").GetRect();
+ Assert.That(topLabelDuringAllRect.Y, Is.EqualTo(topLabelBeforeRect.Y),
+ $"During keyboard (All) - top label Y ({topLabelDuringAllRect.Y}) should remain at ({topLabelBeforeRect.Y})");
+
+ var bottomLabelDuringAllRect = App.WaitForElement("BottomEdgeIndicator").GetRect();
+ Assert.That(bottomLabelDuringAllRect.Bottom, Is.EqualTo(keyboardY),
+ $"During keyboard (All) - bottom label Bottom ({bottomLabelDuringAllRect.Bottom}) should equal keyboard Y ({keyboardY})");
+
+ // ── Switch to None while keyboard is open ──
+ App.Tap("SafeAreaNoneButton");
+ Assert.That(App.FindElement("SafeAreaEdgesValueLabel").GetText(), Is.EqualTo("None"));
+
+ // With None, top goes edge-to-edge; bottom does NOT adjust for keyboard
+ var topLabelDuringNoneRect = App.WaitForElement("TopEdgeIndicator").GetRect();
+ Assert.That(topLabelDuringNoneRect.Y, Is.EqualTo(0),
+ $"During keyboard (None) - top label Y ({topLabelDuringNoneRect.Y}) should be 0 (edge-to-edge)");
+
+#if !ANDROID // On Android, Appium does not find the bottom label when the keyboard is open
+ var bottomLabelDuringNoneRect = App.WaitForElement("BottomEdgeIndicator").GetRect();
+ Assert.That(Math.Abs(bottomLabelDuringNoneRect.Bottom), Is.EqualTo(screenHeight),
+ $"During keyboard (None) - bottom label Bottom ({bottomLabelDuringNoneRect.Bottom}) should be equal to screenHeight ({screenHeight})");
+#endif
+ // ── Dismiss keyboard ──
+ App.DismissKeyboard();
+ App.WaitForKeyboardToHide();
+ Assert.That(App.IsKeyboardShown(), Is.False, "Keyboard should be hidden after dismissal");
+
+ // After keyboard (None): top at 0, bottom at screenHeight
+ var topLabelAfterRect = App.WaitForElement("TopEdgeIndicator").GetRect();
+ Assert.That(topLabelAfterRect.Y, Is.EqualTo(0),
+ $"After keyboard - top label Y ({topLabelAfterRect.Y}) should be 0 (edge-to-edge)");
+
+ var bottomLabelAfterRect = App.WaitForElement("BottomEdgeIndicator").GetRect();
+ Assert.That(Math.Abs(bottomLabelAfterRect.Bottom), Is.EqualTo(screenHeight),
+ $"After keyboard - bottom label Bottom ({bottomLabelAfterRect.Bottom}) should be equal to screenHeight ({screenHeight})");
+ }
+
+ [Test, Order(18)]
+ [Description("Switch Container to SoftInput while keyboard is open — bottom indicator moves up")]
+ public void Validate_ContentView_KeyboardRuntime_SwitchContainerToSoftInput_WhileKeyboardOpen()
+ {
+ ClickContentViewSafeAreaButton();
+ App.DismissKeyboard();
+
+ // Navigating to the Options page to reset the ViewModel to its default settings before the test to ensure consistent testing
+ App.WaitForElement("Options");
+ App.Tap("Options");
+
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+ App.WaitForElement("SafeAreaContainerButton");
+ App.Tap("SafeAreaContainerButton");
+
+ var insets = GetSafeAreaInsets();
+ var (_, screenHeight) = GetScreenSize();
+
+ // ── Before keyboard (Container) ──
+ var topLabelBeforeRect = App.WaitForElement("TopEdgeIndicator").GetRect();
+ Assert.That(Math.Abs(topLabelBeforeRect.Y), Is.EqualTo(insets.Top),
+ $"Before keyboard - top label Y ({topLabelBeforeRect.Y}) should be equal to insets.Top ({insets.Top})");
+
+ var bottomLabelBeforeRect = App.WaitForElement("BottomEdgeIndicator").GetRect();
+ Assert.That(Math.Abs(bottomLabelBeforeRect.Bottom), Is.EqualTo(screenHeight - insets.Bottom),
+ $"Before keyboard - bottom label Bottom ({bottomLabelBeforeRect.Bottom}) should be equal to (screenHeight - insets.Bottom) ({screenHeight - insets.Bottom})");
+
+ // ── Show keyboard (Container) ──
+ Assert.That(App.IsKeyboardShown(), Is.False, "Keyboard should not be visible before tapping entry");
+ App.Tap("SafeAreaTestEntry");
+ App.WaitForKeyboardToShow();
+ Assert.That(App.IsKeyboardShown(), Is.True, "Keyboard should be visible after tapping entry");
+
+ // With Container, bottom should NOT move
+ var topLabelDuringContainerRect = App.WaitForElement("TopEdgeIndicator").GetRect();
+ Assert.That(topLabelDuringContainerRect.Y, Is.EqualTo(topLabelBeforeRect.Y),
+ $"During keyboard (Container) - top label Y ({topLabelDuringContainerRect.Y}) should remain at ({topLabelBeforeRect.Y})");
+
+#if !ANDROID // On Android, Appium does not find the bottom label when the keyboard is open
+ var bottomLabelDuringContainerRect = App.WaitForElement("BottomEdgeIndicator").GetRect();
+ Assert.That(bottomLabelDuringContainerRect.Bottom, Is.EqualTo(screenHeight - insets.Bottom),
+ $"During keyboard (Container) - bottom label Bottom ({bottomLabelDuringContainerRect.Bottom}) should equal (screenHeight - insets.Bottom) ({screenHeight - insets.Bottom})");
+#endif
+ // ── Switch to SoftInput while keyboard is open ──
+ App.Tap("SafeAreaSoftInputButton");
+ Assert.That(App.FindElement("SafeAreaEdgesValueLabel").GetText(), Is.EqualTo("SoftInput"));
+
+ var keyboardY = GetKeyboardY();
+
+ // With SoftInput, bottom should move up to keyboard top
+ var topLabelDuringSoftInputRect = App.WaitForElement("TopEdgeIndicator").GetRect();
+ Assert.That(Math.Abs(topLabelDuringSoftInputRect.Y), Is.EqualTo(insets.Top),
+ $"During keyboard (SoftInput) - top label Y ({topLabelDuringSoftInputRect.Y}) should be equal to insets.Top ({insets.Top})");
+
+ var bottomLabelDuringSoftInputRect = App.WaitForElement("BottomEdgeIndicator").GetRect();
+ Assert.That(bottomLabelDuringSoftInputRect.Bottom, Is.EqualTo(keyboardY),
+ $"During keyboard (SoftInput) - bottom label Bottom ({bottomLabelDuringSoftInputRect.Bottom}) should equal keyboard Y ({keyboardY})");
+
+ // ── Dismiss keyboard ──
+ App.DismissKeyboard();
+ App.WaitForKeyboardToHide();
+ Assert.That(App.IsKeyboardShown(), Is.False, "Keyboard should be hidden after dismissal");
+
+ // After keyboard (SoftInput): top at insets.Top, bottom at screenHeight (edge-to-edge without keyboard)
+ var topLabelAfterRect = App.WaitForElement("TopEdgeIndicator").GetRect();
+ Assert.That(Math.Abs(topLabelAfterRect.Y), Is.EqualTo(insets.Top),
+ $"After keyboard - top label Y ({topLabelAfterRect.Y}) should be equal to insets.Top ({insets.Top})");
+
+ var bottomLabelAfterRect = App.WaitForElement("BottomEdgeIndicator").GetRect();
+ Assert.That(Math.Abs(bottomLabelAfterRect.Bottom), Is.EqualTo(screenHeight),
+ $"After keyboard - bottom label Bottom ({bottomLabelAfterRect.Bottom}) should be equal to screenHeight ({screenHeight})");
+ }
+
+#if TEST_FAILS_ON_IOS // Issue Link - https://github.com/dotnet/maui/issues/34847
+
+ [Test, Order(19)]
+ [Description("Keyboard open: cycle through None → All → Container → SoftInput → Default → None and verify positions")]
+ public void Validate_ContentView_KeyboardRuntime_CycleThroughAllModes_WhileKeyboardOpen()
+ {
+ ClickContentViewSafeAreaButton();
+ App.DismissKeyboard();
+
+ // ── Start with None ──
+ App.WaitForElement("SafeAreaNoneButton");
+ App.Tap("SafeAreaNoneButton");
+ Assert.That(App.FindElement("SafeAreaEdgesValueLabel").GetText(), Is.EqualTo("None"));
+
+ var insets = GetSafeAreaInsets();
+ var (_, screenHeight) = GetScreenSize();
+
+ // ── Verify None positions before keyboard ──
+ var topLabelRect = App.WaitForElement("TopEdgeIndicator").GetRect();
+ Assert.That(topLabelRect.Y, Is.EqualTo(0),
+ $"None (before keyboard) - top label Y ({topLabelRect.Y}) should be 0 (edge-to-edge)");
+
+ var bottomLabelRect = App.WaitForElement("BottomEdgeIndicator").GetRect();
+ Assert.That(Math.Abs(bottomLabelRect.Bottom), Is.EqualTo(screenHeight),
+ $"None (before keyboard) - bottom label Bottom ({bottomLabelRect.Bottom}) should be equal to screenHeight ({screenHeight})");
+
+ // ── Open keyboard ──
+ Assert.That(App.IsKeyboardShown(), Is.False, "Keyboard should not be visible before tapping entry");
+ App.Tap("SafeAreaTestEntry");
+ App.WaitForKeyboardToShow();
+ Assert.That(App.IsKeyboardShown(), Is.True, "Keyboard should be visible after tapping entry");
+
+ var keyboardY = GetKeyboardY();
+
+ // ── Verify None with keyboard (no adjustment) ──
+ topLabelRect = App.WaitForElement("TopEdgeIndicator").GetRect();
+ Assert.That(topLabelRect.Y, Is.EqualTo(0),
+ $"None (keyboard open) - top label Y ({topLabelRect.Y}) should be 0 (edge-to-edge)");
+
+#if !ANDROID // On Android, Appium does not find the bottom label when the keyboard is open
+ bottomLabelRect = App.WaitForElement("BottomEdgeIndicator").GetRect();
+ Assert.That(Math.Abs(bottomLabelRect.Bottom), Is.EqualTo(screenHeight),
+ $"None (keyboard open) - bottom label Bottom ({bottomLabelRect.Bottom}) should be equal to screenHeight ({screenHeight})");
+#endif
+ // ── Switch to All (keyboard still open) ──
+ App.Tap("SafeAreaAllButton");
+ Assert.That(App.FindElement("SafeAreaEdgesValueLabel").GetText(), Is.EqualTo("All"));
+
+ keyboardY = GetKeyboardY();
+
+ topLabelRect = App.WaitForElement("TopEdgeIndicator").GetRect();
+ Assert.That(Math.Abs(topLabelRect.Y), Is.EqualTo(insets.Top),
+ $"All (keyboard open) - top label Y ({topLabelRect.Y}) should be equal to insets.Top ({insets.Top})");
+
+ bottomLabelRect = App.WaitForElement("BottomEdgeIndicator").GetRect();
+ Assert.That(bottomLabelRect.Bottom, Is.EqualTo(keyboardY),
+ $"All (keyboard open) - bottom label Bottom ({bottomLabelRect.Bottom}) should equal keyboard Y ({keyboardY})");
+
+ // ── Switch to Container (keyboard still open) ──
+ App.Tap("SafeAreaContainerButton");
+ Assert.That(App.FindElement("SafeAreaEdgesValueLabel").GetText(), Is.EqualTo("Container"));
+
+ topLabelRect = App.WaitForElement("TopEdgeIndicator").GetRect();
+ Assert.That(Math.Abs(topLabelRect.Y), Is.EqualTo(insets.Top),
+ $"Container (keyboard open) - top label Y ({topLabelRect.Y}) should be equal to insets.Top ({insets.Top})");
+
+#if !ANDROID // On Android, Appium does not find the bottom label when the keyboard is open
+ bottomLabelRect = App.WaitForElement("BottomEdgeIndicator").GetRect();
+ Assert.That(Math.Abs(bottomLabelRect.Bottom), Is.EqualTo(screenHeight - insets.Bottom),
+ $"Container (keyboard open) - bottom label Bottom ({bottomLabelRect.Bottom}) should be equal to (screenHeight - insets.Bottom) ({screenHeight - insets.Bottom})");
+#endif
+ // ── Switch to SoftInput (keyboard still open) ──
+ App.Tap("SafeAreaSoftInputButton");
+ Assert.That(App.FindElement("SafeAreaEdgesValueLabel").GetText(), Is.EqualTo("SoftInput"));
+
+ topLabelRect = App.WaitForElement("TopEdgeIndicator").GetRect();
+ Assert.That(Math.Abs(topLabelRect.Y), Is.EqualTo(insets.Top),
+ $"SoftInput (keyboard open) - top label Y ({topLabelRect.Y}) should be equal to insets.Top ({insets.Top})");
+
+ bottomLabelRect = App.WaitForElement("BottomEdgeIndicator").GetRect();
+ Assert.That(bottomLabelRect.Bottom, Is.EqualTo(keyboardY),
+ $"SoftInput (keyboard open) - bottom label Bottom ({bottomLabelRect.Bottom}) should equal keyboard Y ({keyboardY})");
+
+ // ── Switch to Default (keyboard still open) ──
+ // Default on ContentView resolves to None — edge-to-edge, no safe area padding
+ App.Tap("SafeAreaDefaultButton");
+ Assert.That(App.FindElement("SafeAreaEdgesValueLabel").GetText(), Is.EqualTo("Default"));
+
+ topLabelRect = App.WaitForElement("TopEdgeIndicator").GetRect();
+ Assert.That(topLabelRect.Y, Is.EqualTo(0),
+ $"Default (keyboard open) - top label Y ({topLabelRect.Y}) should be 0 (edge-to-edge, Default on ContentView = None)");
+
+#if !ANDROID // On Android, Appium does not find the bottom label when the keyboard is open
+ bottomLabelRect = App.WaitForElement("BottomEdgeIndicator").GetRect();
+ Assert.That(Math.Abs(bottomLabelRect.Bottom), Is.EqualTo(screenHeight),
+ $"Default (keyboard open) - bottom label Bottom ({bottomLabelRect.Bottom}) should be equal to screenHeight ({screenHeight})");
+#endif
+ // ── Switch back to None (keyboard still open) ──
+ App.Tap("SafeAreaNoneButton");
+ Assert.That(App.FindElement("SafeAreaEdgesValueLabel").GetText(), Is.EqualTo("None"));
+
+ topLabelRect = App.WaitForElement("TopEdgeIndicator").GetRect();
+ Assert.That(topLabelRect.Y, Is.EqualTo(0),
+ $"None (keyboard open, after cycle) - top label Y ({topLabelRect.Y}) should be 0 (edge-to-edge)");
+
+#if !ANDROID // On Android, Appium does not find the bottom label when the keyboard is open
+ bottomLabelRect = App.WaitForElement("BottomEdgeIndicator").GetRect();
+ Assert.That(Math.Abs(bottomLabelRect.Bottom), Is.EqualTo(screenHeight),
+ $"None (keyboard open, after cycle) - bottom label Bottom ({bottomLabelRect.Bottom}) should be equal to screenHeight ({screenHeight})");
+#endif
+ // ── Dismiss keyboard ──
+ App.DismissKeyboard();
+ App.WaitForKeyboardToHide();
+ Assert.That(App.IsKeyboardShown(), Is.False, "Keyboard should be hidden after dismissal");
+ }
+#endif
+
+ // ──────────────────────────────────────────────
+ // Interaction with ContentView Properties
+ // ──────────────────────────────────────────────
+
+ [Test, Order(20)]
+ [Description("Safe area insets and padding are additive")]
+ public void Validate_ContentView_SafeArea_WithPadding()
+ {
+ ClickContentViewSafeAreaButton();
+
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("UniformAll");
+ App.Tap("UniformAll");
+ App.Tap("PaddingCheckBox");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+
+ App.WaitForElement("SafeAreaEdgesValueLabel");
+ Assert.That(App.FindElement("SafeAreaEdgesValueLabel").GetText(), Is.EqualTo("All"));
+
+ var insets = GetSafeAreaInsets();
+
+ // With All + padding, top should be beyond safe area inset (additive)
+ var topLabelRect = App.WaitForElement("TopEdgeIndicator").GetRect();
+ Assert.That(topLabelRect.Y, Is.GreaterThan(insets.Top),
+ $"Top Y ({topLabelRect.Y}) should be > insets.Top ({insets.Top}) due to additional padding");
+ }
+
+ [Test, Order(21)]
+ [Description("Background extends edge-to-edge behind system UI")]
+ public void Validate_ContentView_SafeArea_None_WithBackground()
+ {
+ ClickContentViewSafeAreaButton();
+
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("UniformNone");
+ App.Tap("UniformNone");
+ App.Tap("BackgroundCheckBox");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+
+ App.WaitForElement("SafeAreaEdgesValueLabel");
+ Assert.That(App.FindElement("SafeAreaEdgesValueLabel").GetText(), Is.EqualTo("None"));
+ }
+
+ // ──────────────────────────────────────────────
+ // Orientation / Landscape Validation
+ // ──────────────────────────────────────────────
+
+ [Test, Order(22)]
+ [Description("None: landscape left/right/bottom all edge-to-edge")]
+ public void Validate_ContentView_Orientation_None_Landscape()
+ {
+ ClickContentViewSafeAreaButton();
+
+ App.WaitForElement("SafeAreaNoneButton");
+ App.Tap("SafeAreaNoneButton");
+ Assert.That(App.FindElement("SafeAreaEdgesValueLabel").GetText(), Is.EqualTo("None"));
+
+ App.SetOrientationLandscape();
+ Thread.Sleep(1000);
+
+ var (screenWidth, screenHeight) = GetScreenSize();
+
+ // Left: edge-to-edge
+ var leftRect = App.WaitForElement("LeftEdgeIndicator").GetRect();
+ Assert.That(leftRect.X, Is.EqualTo(0),
+ $"None: left X ({leftRect.X}) should be = 0 (edge-to-edge)");
+
+ // Right: edge-to-edge
+ var rightRect = App.WaitForElement("RightEdgeIndicator").GetRect();
+ var rightEdge = rightRect.X + rightRect.Width;
+ Assert.That(Math.Abs(rightEdge), Is.EqualTo(screenWidth),
+ $"None: right edge ({rightEdge}) should be = screenWidth ({screenWidth})");
+
+ // Bottom: edge-to-edge
+ var bottomRect = App.WaitForElement("BottomEdgeIndicator").GetRect();
+ Assert.That(Math.Abs(bottomRect.Bottom), Is.EqualTo(screenHeight),
+ $"None: bottom edge ({bottomRect.Bottom}) should be = screenHeight ({screenHeight})");
+
+ App.SetOrientationPortrait();
+ Thread.Sleep(1000);
+ }
+
+ [Test, Order(23)]
+ [Description("All: landscape left/right/bottom inset by safe area")]
+ public void Validate_ContentView_Orientation_All_Landscape()
+ {
+ ClickContentViewSafeAreaButton();
+
+ App.WaitForElement("SafeAreaAllButton");
+ App.Tap("SafeAreaAllButton");
+ Assert.That(App.FindElement("SafeAreaEdgesValueLabel").GetText(), Is.EqualTo("All"));
+
+ App.SetOrientationLandscape();
+ Thread.Sleep(1000);
+
+ var (screenWidth, screenHeight) = GetScreenSize();
+ var insetsLandscape = GetSafeAreaInsets();
+
+ // Left: inset by safe area
+ var leftRect = App.WaitForElement("LeftEdgeIndicator").GetRect();
+ Assert.That(Math.Abs(leftRect.X), Is.EqualTo(insetsLandscape.Left),
+ $"All: left X ({leftRect.X}) should be = insetsLandscape.Left ({insetsLandscape.Left})");
+
+ // Right: inset by safe area (Android uses display cutout for right inset)
+ var rightRect = App.WaitForElement("RightEdgeIndicator").GetRect();
+ var rightEdge = rightRect.X + rightRect.Width;
+ var expectedRight = GetLandscapeRightInset(insetsLandscape.Right, insetsLandscape.CutoutR);
+ Assert.That(Math.Abs(rightEdge), Is.EqualTo(screenWidth - expectedRight),
+ $"All: right edge ({rightEdge}) should be = screenWidth - expectedRight ({screenWidth - expectedRight})");
+
+ // Bottom: inset by safe area
+ var bottomRect = App.WaitForElement("BottomEdgeIndicator").GetRect();
+ Assert.That(Math.Abs(bottomRect.Bottom), Is.EqualTo(screenHeight - insetsLandscape.Bottom),
+ $"All: bottom edge ({bottomRect.Bottom}) should be = screenHeight - insetsLandscape.Bottom ({screenHeight - insetsLandscape.Bottom})");
+
+ App.SetOrientationPortrait();
+ Thread.Sleep(1000);
+ }
+
+ [Test, Order(24)]
+ [Description("Container: landscape left/right/bottom inset by safe area")]
+ public void Validate_ContentView_Orientation_Container_Landscape()
+ {
+ ClickContentViewSafeAreaButton();
+
+ App.WaitForElement("SafeAreaContainerButton");
+ App.Tap("SafeAreaContainerButton");
+ Assert.That(App.FindElement("SafeAreaEdgesValueLabel").GetText(), Is.EqualTo("Container"));
+
+ App.SetOrientationLandscape();
+ Thread.Sleep(1000);
+
+ var (screenWidth, screenHeight) = GetScreenSize();
+ var insetsLandscape = GetSafeAreaInsets();
+
+ // Left: inset by safe area
+ var leftRect = App.WaitForElement("LeftEdgeIndicator").GetRect();
+ Assert.That(Math.Abs(leftRect.X), Is.EqualTo(insetsLandscape.Left),
+ $"Container: left X ({leftRect.X}) should be = insetsLandscape.Left ({insetsLandscape.Left})");
+
+ // Right: inset by safe area (Android uses display cutout for right inset)
+ var rightRect = App.WaitForElement("RightEdgeIndicator").GetRect();
+ var rightEdge = rightRect.X + rightRect.Width;
+ var expectedRight = GetLandscapeRightInset(insetsLandscape.Right, insetsLandscape.CutoutR);
+ Assert.That(Math.Abs(rightEdge), Is.EqualTo(screenWidth - expectedRight),
+ $"Container: right edge ({rightEdge}) should be = screenWidth - expectedRight ({screenWidth - expectedRight})");
+
+ // Bottom: inset by safe area
+ var bottomRect = App.WaitForElement("BottomEdgeIndicator").GetRect();
+ Assert.That(Math.Abs(bottomRect.Bottom), Is.EqualTo(screenHeight - insetsLandscape.Bottom),
+ $"Container: bottom edge ({bottomRect.Bottom}) should be = screenHeight - insetsLandscape.Bottom ({screenHeight - insetsLandscape.Bottom})");
+
+ App.SetOrientationPortrait();
+ Thread.Sleep(1000);
+ }
+
+ [Test, Order(25)]
+ [Description("SoftInput: landscape left/right inset by safe area, bottom edge-to-edge")]
+ public void Validate_ContentView_Orientation_SoftInput_Landscape()
+ {
+ ClickContentViewSafeAreaButton();
+
+ App.WaitForElement("SafeAreaSoftInputButton");
+ App.Tap("SafeAreaSoftInputButton");
+ Assert.That(App.FindElement("SafeAreaEdgesValueLabel").GetText(), Is.EqualTo("SoftInput"));
+
+ App.SetOrientationLandscape();
+ Thread.Sleep(1000);
+
+ var (screenWidth, screenHeight) = GetScreenSize();
+ var insetsLandscape = GetSafeAreaInsets();
+
+ // Left: inset by safe area
+ var leftRect = App.WaitForElement("LeftEdgeIndicator").GetRect();
+ Assert.That(Math.Abs(leftRect.X), Is.EqualTo(insetsLandscape.Left),
+ $"SoftInput: left X ({leftRect.X}) should be = insetsLandscape.Left ({insetsLandscape.Left})");
+
+ // Right: inset by safe area (Android uses display cutout for right inset)
+ var rightRect = App.WaitForElement("RightEdgeIndicator").GetRect();
+ var rightEdge = rightRect.X + rightRect.Width;
+ var expectedRight = GetLandscapeRightInset(insetsLandscape.Right, insetsLandscape.CutoutR);
+ Assert.That(Math.Abs(rightEdge), Is.EqualTo(screenWidth - expectedRight),
+ $"SoftInput: right edge ({rightEdge}) should be = screenWidth - expectedRight ({screenWidth - expectedRight})");
+
+ // Bottom: edge-to-edge (SoftInput doesn't avoid bottom without keyboard)
+ var bottomRect = App.WaitForElement("BottomEdgeIndicator").GetRect();
+ Assert.That(Math.Abs(bottomRect.Bottom), Is.EqualTo(screenHeight),
+ $"SoftInput: bottom edge ({bottomRect.Bottom}) should be = screenHeight ({screenHeight})");
+
+ App.SetOrientationPortrait();
+ Thread.Sleep(1000);
+ }
+
+ [Test, Order(26)]
+ [Description("Default: landscape all edges edge-to-edge (Default on ContentView resolves to None)")]
+ public void Validate_ContentView_Orientation_Default_Landscape()
+ {
+ ClickContentViewSafeAreaButton();
+
+ App.WaitForElement("SafeAreaDefaultButton");
+ App.Tap("SafeAreaDefaultButton");
+ Assert.That(App.FindElement("SafeAreaEdgesValueLabel").GetText(), Is.EqualTo("Default"));
+
+ App.SetOrientationLandscape();
+ Thread.Sleep(1000);
+
+ var (screenWidth, screenHeight) = GetScreenSize();
+
+ // Left: edge-to-edge
+ var leftRect = App.WaitForElement("LeftEdgeIndicator").GetRect();
+ Assert.That(leftRect.X, Is.EqualTo(0),
+ $"Default: left X ({leftRect.X}) should be = 0 (edge-to-edge)");
+
+ // Right: edge-to-edge
+ var rightRect = App.WaitForElement("RightEdgeIndicator").GetRect();
+ var rightEdge = rightRect.X + rightRect.Width;
+ Assert.That(Math.Abs(rightEdge), Is.EqualTo(screenWidth),
+ $"Default: right edge ({rightEdge}) should be = screenWidth ({screenWidth})");
+
+ // Bottom: edge-to-edge
+ var bottomRect = App.WaitForElement("BottomEdgeIndicator").GetRect();
+ Assert.That(Math.Abs(bottomRect.Bottom), Is.EqualTo(screenHeight),
+ $"Default: bottom edge ({bottomRect.Bottom}) should be = screenHeight ({screenHeight})");
+
+ App.SetOrientationPortrait();
+ Thread.Sleep(1000);
+ }
+
+ // ──────────────────────────────────────────────
+ // Landscape Keyboard Position Validation
+ // ──────────────────────────────────────────────
+
+#if TEST_FAILS_ON_ANDROID // In landscape mode on Android, the keyboard covers the entire screen, and Appium cannot find elements to validate their positions
+
+ [Test, Order(27)]
+ [Description("Landscape All: bottom moves up to keyboard, left/right stay inset")]
+ public void Validate_ContentView_Keyboard_All_Landscape()
+ {
+ ClickContentViewSafeAreaButton();
+ App.DismissKeyboard();
+
+ App.WaitForElement("SafeAreaAllButton");
+ App.Tap("SafeAreaAllButton");
+ Assert.That(App.FindElement("SafeAreaEdgesValueLabel").GetText(), Is.EqualTo("All"));
+
+ App.SetOrientationLandscape();
+ Thread.Sleep(1000);
+
+ var (screenWidth, screenHeight) = GetScreenSize();
+ var insetsLandscape = GetSafeAreaInsets();
+
+ // ── Before keyboard ──
+ var leftBeforeRect = App.WaitForElement("LeftEdgeIndicator").GetRect();
+ Assert.That(Math.Abs(leftBeforeRect.X), Is.EqualTo(insetsLandscape.Left),
+ $"Before keyboard - left X ({leftBeforeRect.X}) should be = insetsLandscape.Left ({insetsLandscape.Left})");
+
+ var rightBeforeRect = App.WaitForElement("RightEdgeIndicator").GetRect();
+ var rightBeforeEdge = rightBeforeRect.X + rightBeforeRect.Width;
+ Assert.That(Math.Abs(rightBeforeEdge), Is.EqualTo(screenWidth - insetsLandscape.Right),
+ $"Before keyboard - right edge ({rightBeforeEdge}) should be = screenWidth - insetsLandscape.Right ({screenWidth - insetsLandscape.Right})");
+
+ var bottomBeforeRect = App.WaitForElement("BottomEdgeIndicator").GetRect();
+ Assert.That(Math.Abs(bottomBeforeRect.Bottom), Is.EqualTo(screenHeight - insetsLandscape.Bottom),
+ $"Before keyboard - bottom edge ({bottomBeforeRect.Bottom}) should be = screenHeight - insetsLandscape.Bottom ({screenHeight - insetsLandscape.Bottom})");
+
+ // ── Show keyboard ──
+ Assert.That(App.IsKeyboardShown(), Is.False, "Keyboard should not be visible before tapping entry");
+ App.Tap("SafeAreaTestEntry");
+ App.WaitForKeyboardToShow();
+ Assert.That(App.IsKeyboardShown(), Is.True, "Keyboard should be visible after tapping entry");
+
+ var keyboardY = GetKeyboardY();
+
+ // Bottom should move up to keyboard top
+ var bottomDuringRect = App.WaitForElement("BottomEdgeIndicator").GetRect();
+ Assert.That(bottomDuringRect.Bottom, Is.EqualTo(keyboardY).Within(1),
+ $"During keyboard - bottom edge ({bottomDuringRect.Bottom}) should equal keyboard Y ({keyboardY})");
+
+ // Left/Right should remain unchanged
+ var leftDuringRect = App.WaitForElement("LeftEdgeIndicator").GetRect();
+ Assert.That(leftDuringRect.X, Is.EqualTo(leftBeforeRect.X),
+ $"During keyboard - left X ({leftDuringRect.X}) should remain at ({leftBeforeRect.X})");
+
+ var rightDuringRect = App.WaitForElement("RightEdgeIndicator").GetRect();
+ var rightDuringEdge = rightDuringRect.X + rightDuringRect.Width;
+ Assert.That(rightDuringEdge, Is.EqualTo(rightBeforeEdge),
+ $"During keyboard - right edge ({rightDuringEdge}) should remain at ({rightBeforeEdge})");
+
+ // ── Dismiss keyboard ──
+ App.DismissKeyboard();
+ App.WaitForKeyboardToHide();
+ Assert.That(App.IsKeyboardShown(), Is.False, "Keyboard should be hidden after dismissal");
+
+ // All edges should return to original positions
+ var leftAfterRect = App.WaitForElement("LeftEdgeIndicator").GetRect();
+ Assert.That(leftAfterRect.X, Is.EqualTo(leftBeforeRect.X),
+ $"After keyboard - left X ({leftAfterRect.X}) should return to original ({leftBeforeRect.X})");
+
+ var rightAfterRect = App.WaitForElement("RightEdgeIndicator").GetRect();
+ var rightAfterEdge = rightAfterRect.X + rightAfterRect.Width;
+ Assert.That(rightAfterEdge, Is.EqualTo(rightBeforeEdge),
+ $"After keyboard - right edge ({rightAfterEdge}) should return to original ({rightBeforeEdge})");
+
+ var bottomAfterRect = App.WaitForElement("BottomEdgeIndicator").GetRect();
+ Assert.That(bottomAfterRect.Bottom, Is.EqualTo(bottomBeforeRect.Bottom),
+ $"After keyboard - bottom edge ({bottomAfterRect.Bottom}) should return to original ({bottomBeforeRect.Bottom})");
+
+ App.SetOrientationPortrait();
+ Thread.Sleep(1000);
+ }
+
+ [Test, Order(28)]
+ [Description("Landscape SoftInput: bottom moves up to keyboard, left/right stay inset")]
+ public void Validate_ContentView_Keyboard_SoftInput_Landscape()
+ {
+ ClickContentViewSafeAreaButton();
+ App.DismissKeyboard();
+
+ App.WaitForElement("SafeAreaSoftInputButton");
+ App.Tap("SafeAreaSoftInputButton");
+ Assert.That(App.FindElement("SafeAreaEdgesValueLabel").GetText(), Is.EqualTo("SoftInput"));
+
+ App.SetOrientationLandscape();
+ Thread.Sleep(1000);
+
+ var (screenWidth, screenHeight) = GetScreenSize();
+ var insetsLandscape = GetSafeAreaInsets();
+
+ // ── Before keyboard ──
+ var leftBeforeRect = App.WaitForElement("LeftEdgeIndicator").GetRect();
+ Assert.That(Math.Abs(leftBeforeRect.X), Is.EqualTo(insetsLandscape.Left),
+ $"Before keyboard - left X ({leftBeforeRect.X}) should be = insetsLandscape.Left ({insetsLandscape.Left})");
+
+ var rightBeforeRect = App.WaitForElement("RightEdgeIndicator").GetRect();
+ var rightBeforeEdge = rightBeforeRect.X + rightBeforeRect.Width;
+ Assert.That(Math.Abs(rightBeforeEdge), Is.EqualTo(screenWidth - insetsLandscape.Right),
+ $"Before keyboard - right edge ({rightBeforeEdge}) should be = screenWidth - insetsLandscape.Right ({screenWidth - insetsLandscape.Right})");
+
+ var bottomBeforeRect = App.WaitForElement("BottomEdgeIndicator").GetRect();
+ Assert.That(Math.Abs(bottomBeforeRect.Bottom), Is.EqualTo(screenHeight),
+ $"Before keyboard - bottom edge ({bottomBeforeRect.Bottom}) should be = screenHeight ({screenHeight})");
+
+ // ── Show keyboard ──
+ Assert.That(App.IsKeyboardShown(), Is.False, "Keyboard should not be visible before tapping entry");
+ App.Tap("SafeAreaTestEntry");
+ App.WaitForKeyboardToShow();
+ Assert.That(App.IsKeyboardShown(), Is.True, "Keyboard should be visible after tapping entry");
+
+ var keyboardY = GetKeyboardY();
+
+ // Bottom should move up to keyboard top
+ var bottomDuringRect = App.WaitForElement("BottomEdgeIndicator").GetRect();
+ Assert.That(bottomDuringRect.Bottom, Is.EqualTo(keyboardY).Within(1),
+ $"During keyboard - bottom edge ({bottomDuringRect.Bottom}) should equal keyboard Y ({keyboardY})");
+
+ // Left/Right should remain unchanged
+ var leftDuringRect = App.WaitForElement("LeftEdgeIndicator").GetRect();
+ Assert.That(leftDuringRect.X, Is.EqualTo(leftBeforeRect.X),
+ $"During keyboard - left X ({leftDuringRect.X}) should remain at ({leftBeforeRect.X})");
+
+ var rightDuringRect = App.WaitForElement("RightEdgeIndicator").GetRect();
+ var rightDuringEdge = rightDuringRect.X + rightDuringRect.Width;
+ Assert.That(rightDuringEdge, Is.EqualTo(rightBeforeEdge),
+ $"During keyboard - right edge ({rightDuringEdge}) should remain at ({rightBeforeEdge})");
+
+ // ── Dismiss keyboard ──
+ App.DismissKeyboard();
+ App.WaitForKeyboardToHide();
+ Assert.That(App.IsKeyboardShown(), Is.False, "Keyboard should be hidden after dismissal");
+
+ // All edges should return to original positions
+ var leftAfterRect = App.WaitForElement("LeftEdgeIndicator").GetRect();
+ Assert.That(leftAfterRect.X, Is.EqualTo(leftBeforeRect.X),
+ $"After keyboard - left X ({leftAfterRect.X}) should return to original ({leftBeforeRect.X})");
+
+ var rightAfterRect = App.WaitForElement("RightEdgeIndicator").GetRect();
+ var rightAfterEdge = rightAfterRect.X + rightAfterRect.Width;
+ Assert.That(rightAfterEdge, Is.EqualTo(rightBeforeEdge),
+ $"After keyboard - right edge ({rightAfterEdge}) should return to original ({rightBeforeEdge})");
+
+ var bottomAfterRect = App.WaitForElement("BottomEdgeIndicator").GetRect();
+ Assert.That(bottomAfterRect.Bottom, Is.EqualTo(bottomBeforeRect.Bottom),
+ $"After keyboard - bottom edge ({bottomAfterRect.Bottom}) should return to original ({bottomBeforeRect.Bottom})");
+
+ App.SetOrientationPortrait();
+ Thread.Sleep(1000);
+ }
+
+ [Test, Order(29)]
+ [Description("Landscape None: bottom stays at screen edge with keyboard, left/right stay edge-to-edge")]
+ public void Validate_ContentView_Keyboard_None_Landscape()
+ {
+ ClickContentViewSafeAreaButton();
+ App.DismissKeyboard();
+
+ App.WaitForElement("SafeAreaNoneButton");
+ App.Tap("SafeAreaNoneButton");
+ Assert.That(App.FindElement("SafeAreaEdgesValueLabel").GetText(), Is.EqualTo("None"));
+
+ App.SetOrientationLandscape();
+ Thread.Sleep(1000);
+
+ var (screenWidth, screenHeight) = GetScreenSize();
+
+ // ── Before keyboard ──
+ var leftBeforeRect = App.WaitForElement("LeftEdgeIndicator").GetRect();
+ Assert.That(leftBeforeRect.X, Is.EqualTo(0),
+ $"Before keyboard - left X ({leftBeforeRect.X}) should be = 0 (edge-to-edge)");
+
+ var rightBeforeRect = App.WaitForElement("RightEdgeIndicator").GetRect();
+ var rightBeforeEdge = rightBeforeRect.X + rightBeforeRect.Width;
+ Assert.That(Math.Abs(rightBeforeEdge), Is.EqualTo(screenWidth),
+ $"Before keyboard - right edge ({rightBeforeEdge}) should be = screenWidth ({screenWidth})");
+
+ var bottomBeforeRect = App.WaitForElement("BottomEdgeIndicator").GetRect();
+ Assert.That(Math.Abs(bottomBeforeRect.Bottom), Is.EqualTo(screenHeight),
+ $"Before keyboard - bottom edge ({bottomBeforeRect.Bottom}) should be = screenHeight ({screenHeight})");
+
+ // ── Show keyboard ──
+ Assert.That(App.IsKeyboardShown(), Is.False, "Keyboard should not be visible before tapping entry");
+ App.Tap("SafeAreaTestEntry");
+ App.WaitForKeyboardToShow();
+ Assert.That(App.IsKeyboardShown(), Is.True, "Keyboard should be visible after tapping entry");
+
+ // Bottom should NOT move (None ignores keyboard)
+ var bottomDuringRect = App.WaitForElement("BottomEdgeIndicator").GetRect();
+ Assert.That(Math.Abs(bottomDuringRect.Bottom), Is.EqualTo(screenHeight),
+ $"During keyboard - bottom edge ({bottomDuringRect.Bottom}) should remain at screenHeight ({screenHeight})");
+
+ // Left/Right should remain unchanged
+ var leftDuringRect = App.WaitForElement("LeftEdgeIndicator").GetRect();
+ Assert.That(leftDuringRect.X, Is.EqualTo(0),
+ $"During keyboard - left X ({leftDuringRect.X}) should remain at 0 (edge-to-edge)");
+
+ var rightDuringRect = App.WaitForElement("RightEdgeIndicator").GetRect();
+ var rightDuringEdge = rightDuringRect.X + rightDuringRect.Width;
+ Assert.That(Math.Abs(rightDuringEdge), Is.EqualTo(screenWidth),
+ $"During keyboard - right edge ({rightDuringEdge}) should remain at screenWidth ({screenWidth})");
+
+ // ── Dismiss keyboard ──
+ App.DismissKeyboard();
+ App.WaitForKeyboardToHide();
+ Assert.That(App.IsKeyboardShown(), Is.False, "Keyboard should be hidden after dismissal");
+
+ var leftAfterRect = App.WaitForElement("LeftEdgeIndicator").GetRect();
+ Assert.That(leftAfterRect.X, Is.EqualTo(leftBeforeRect.X),
+ $"After keyboard - left X ({leftAfterRect.X}) should return to original ({leftBeforeRect.X})");
+
+ var rightAfterRect = App.WaitForElement("RightEdgeIndicator").GetRect();
+ var rightAfterEdge = rightAfterRect.X + rightAfterRect.Width;
+ Assert.That(rightAfterEdge, Is.EqualTo(rightBeforeEdge),
+ $"After keyboard - right edge ({rightAfterEdge}) should return to original ({rightBeforeEdge})");
+
+ var bottomAfterRect = App.WaitForElement("BottomEdgeIndicator").GetRect();
+ Assert.That(bottomAfterRect.Bottom, Is.EqualTo(bottomBeforeRect.Bottom),
+ $"After keyboard - bottom edge ({bottomAfterRect.Bottom}) should return to original ({bottomBeforeRect.Bottom})");
+
+ App.SetOrientationPortrait();
+ Thread.Sleep(1000);
+ }
+
+ [Test, Order(30)]
+ [Description("Landscape Container: bottom stays at safe area inset with keyboard, left/right stay inset")]
+ public void Validate_ContentView_Keyboard_Container_Landscape()
+ {
+ ClickContentViewSafeAreaButton();
+ App.DismissKeyboard();
+
+ App.WaitForElement("SafeAreaContainerButton");
+ App.Tap("SafeAreaContainerButton");
+ Assert.That(App.FindElement("SafeAreaEdgesValueLabel").GetText(), Is.EqualTo("Container"));
+
+ App.SetOrientationLandscape();
+ Thread.Sleep(1000);
+
+ var (screenWidth, screenHeight) = GetScreenSize();
+ var insetsLandscape = GetSafeAreaInsets();
+
+ // ── Before keyboard ──
+ var leftBeforeRect = App.WaitForElement("LeftEdgeIndicator").GetRect();
+ Assert.That(Math.Abs(leftBeforeRect.X), Is.EqualTo(insetsLandscape.Left),
+ $"Before keyboard - left X ({leftBeforeRect.X}) should be = insetsLandscape.Left ({insetsLandscape.Left})");
+
+ var rightBeforeRect = App.WaitForElement("RightEdgeIndicator").GetRect();
+ var rightBeforeEdge = rightBeforeRect.X + rightBeforeRect.Width;
+ Assert.That(Math.Abs(rightBeforeEdge), Is.EqualTo(screenWidth - insetsLandscape.Right),
+ $"Before keyboard - right edge ({rightBeforeEdge}) should be = screenWidth - insetsLandscape.Right ({screenWidth - insetsLandscape.Right})");
+
+ var bottomBeforeRect = App.WaitForElement("BottomEdgeIndicator").GetRect();
+ Assert.That(Math.Abs(bottomBeforeRect.Bottom), Is.EqualTo(screenHeight - insetsLandscape.Bottom),
+ $"Before keyboard - bottom edge ({bottomBeforeRect.Bottom}) should be = screenHeight - insetsLandscape.Bottom ({screenHeight - insetsLandscape.Bottom})");
+
+ // ── Show keyboard ──
+ Assert.That(App.IsKeyboardShown(), Is.False, "Keyboard should not be visible before tapping entry");
+ App.Tap("SafeAreaTestEntry");
+ App.WaitForKeyboardToShow();
+ Assert.That(App.IsKeyboardShown(), Is.True, "Keyboard should be visible after tapping entry");
+
+ // Bottom should NOT move (Container ignores keyboard)
+ var bottomDuringRect = App.WaitForElement("BottomEdgeIndicator").GetRect();
+ Assert.That(bottomDuringRect.Bottom, Is.EqualTo(bottomBeforeRect.Bottom),
+ $"During keyboard - bottom edge ({bottomDuringRect.Bottom}) should remain at ({bottomBeforeRect.Bottom})");
+
+ // Left/Right should remain unchanged
+ var leftDuringRect = App.WaitForElement("LeftEdgeIndicator").GetRect();
+ Assert.That(leftDuringRect.X, Is.EqualTo(leftBeforeRect.X),
+ $"During keyboard - left X ({leftDuringRect.X}) should remain at ({leftBeforeRect.X})");
+
+ var rightDuringRect = App.WaitForElement("RightEdgeIndicator").GetRect();
+ var rightDuringEdge = rightDuringRect.X + rightDuringRect.Width;
+ Assert.That(rightDuringEdge, Is.EqualTo(rightBeforeEdge),
+ $"During keyboard - right edge ({rightDuringEdge}) should remain at ({rightBeforeEdge})");
+
+ // ── Dismiss keyboard ──
+ App.DismissKeyboard();
+ App.WaitForKeyboardToHide();
+ Assert.That(App.IsKeyboardShown(), Is.False, "Keyboard should be hidden after dismissal");
+
+ var leftAfterRect = App.WaitForElement("LeftEdgeIndicator").GetRect();
+ Assert.That(leftAfterRect.X, Is.EqualTo(leftBeforeRect.X),
+ $"After keyboard - left X ({leftAfterRect.X}) should return to original ({leftBeforeRect.X})");
+
+ var rightAfterRect = App.WaitForElement("RightEdgeIndicator").GetRect();
+ var rightAfterEdge = rightAfterRect.X + rightAfterRect.Width;
+ Assert.That(rightAfterEdge, Is.EqualTo(rightBeforeEdge),
+ $"After keyboard - right edge ({rightAfterEdge}) should return to original ({rightBeforeEdge})");
+
+ var bottomAfterRect = App.WaitForElement("BottomEdgeIndicator").GetRect();
+ Assert.That(bottomAfterRect.Bottom, Is.EqualTo(bottomBeforeRect.Bottom),
+ $"After keyboard - bottom edge ({bottomAfterRect.Bottom}) should return to original ({bottomBeforeRect.Bottom})");
+
+ App.SetOrientationPortrait();
+ Thread.Sleep(1000);
+ }
+
+ [Test, Order(31)]
+ [Description("Landscape Default: all edges edge-to-edge with keyboard (Default on ContentView resolves to None)")]
+ public void Validate_ContentView_Keyboard_Default_Landscape()
+ {
+ ClickContentViewSafeAreaButton();
+ App.DismissKeyboard();
+
+ App.WaitForElement("SafeAreaDefaultButton");
+ App.Tap("SafeAreaDefaultButton");
+ Assert.That(App.FindElement("SafeAreaEdgesValueLabel").GetText(), Is.EqualTo("Default"));
+
+ App.SetOrientationLandscape();
+ Thread.Sleep(1000);
+
+ var (screenWidth, screenHeight) = GetScreenSize();
+
+ // ── Before keyboard ──
+ var leftBeforeRect = App.WaitForElement("LeftEdgeIndicator").GetRect();
+ Assert.That(leftBeforeRect.X, Is.EqualTo(0),
+ $"Before keyboard - left X ({leftBeforeRect.X}) should be = 0 (edge-to-edge)");
+
+ var rightBeforeRect = App.WaitForElement("RightEdgeIndicator").GetRect();
+ var rightBeforeEdge = rightBeforeRect.X + rightBeforeRect.Width;
+ Assert.That(Math.Abs(rightBeforeEdge), Is.EqualTo(screenWidth),
+ $"Before keyboard - right edge ({rightBeforeEdge}) should be = screenWidth ({screenWidth})");
+
+ var bottomBeforeRect = App.WaitForElement("BottomEdgeIndicator").GetRect();
+ Assert.That(Math.Abs(bottomBeforeRect.Bottom), Is.EqualTo(screenHeight),
+ $"Before keyboard - bottom edge ({bottomBeforeRect.Bottom}) should be = screenHeight ({screenHeight})");
+
+ // ── Show keyboard ──
+ Assert.That(App.IsKeyboardShown(), Is.False, "Keyboard should not be visible before tapping entry");
+ App.Tap("SafeAreaTestEntry");
+ App.WaitForKeyboardToShow();
+ Assert.That(App.IsKeyboardShown(), Is.True, "Keyboard should be visible after tapping entry");
+
+ // Bottom should NOT move (Default on ContentView = None, ignores keyboard)
+ var bottomDuringRect = App.WaitForElement("BottomEdgeIndicator").GetRect();
+ Assert.That(bottomDuringRect.Bottom, Is.EqualTo(bottomBeforeRect.Bottom),
+ $"During keyboard - bottom edge ({bottomDuringRect.Bottom}) should remain at ({bottomBeforeRect.Bottom})");
+
+ // Left/Right should remain unchanged
+ var leftDuringRect = App.WaitForElement("LeftEdgeIndicator").GetRect();
+ Assert.That(leftDuringRect.X, Is.EqualTo(leftBeforeRect.X),
+ $"During keyboard - left X ({leftDuringRect.X}) should remain at ({leftBeforeRect.X})");
+
+ var rightDuringRect = App.WaitForElement("RightEdgeIndicator").GetRect();
+ var rightDuringEdge = rightDuringRect.X + rightDuringRect.Width;
+ Assert.That(rightDuringEdge, Is.EqualTo(rightBeforeEdge),
+ $"During keyboard - right edge ({rightDuringEdge}) should remain at ({rightBeforeEdge})");
+
+ // ── Dismiss keyboard ──
+ App.DismissKeyboard();
+ App.WaitForKeyboardToHide();
+ Assert.That(App.IsKeyboardShown(), Is.False, "Keyboard should be hidden after dismissal");
+
+ var bottomAfterRect = App.WaitForElement("BottomEdgeIndicator").GetRect();
+ Assert.That(bottomAfterRect.Bottom, Is.EqualTo(bottomBeforeRect.Bottom),
+ $"After keyboard - bottom edge ({bottomAfterRect.Bottom}) should return to original ({bottomBeforeRect.Bottom})");
+
+ App.SetOrientationPortrait();
+ Thread.Sleep(1000);
+ }
+#endif
+
+ // ──────────────────────────────────────────────
+ // Default + Keyboard (Portrait)
+ // ──────────────────────────────────────────────
+
+ [Test, Order(32)]
+ [Description("With Default, bottom indicator does NOT move when keyboard is shown (Default on ContentView = None)")]
+ public void Validate_ContentView_Keyboard_Default_BottomStays()
+ {
+ ClickContentViewSafeAreaButton();
+ App.DismissKeyboard();
+
+ App.WaitForElement("SafeAreaDefaultButton");
+ App.Tap("SafeAreaDefaultButton");
+
+ Assert.That(App.FindElement("SafeAreaEdgesValueLabel").GetText(), Is.EqualTo("Default"));
+
+ var (_, screenHeight) = GetScreenSize();
+
+ // ── Before keyboard ──
+ var topLabelBeforeRect = App.WaitForElement("TopEdgeIndicator").GetRect();
+ Assert.That(topLabelBeforeRect.Y, Is.EqualTo(0),
+ $"Before keyboard - top label Y ({topLabelBeforeRect.Y}) should be 0 (edge-to-edge, Default on ContentView = None)");
+
+ var bottomLabelBeforeRect = App.WaitForElement("BottomEdgeIndicator").GetRect();
+ Assert.That(Math.Abs(bottomLabelBeforeRect.Bottom), Is.EqualTo(screenHeight),
+ $"Before keyboard - bottom label Bottom ({bottomLabelBeforeRect.Bottom}) should be equal to screenHeight ({screenHeight})");
+
+ Assert.That(App.IsKeyboardShown(), Is.False, "Keyboard should not be visible before tapping entry");
+ App.Tap("SafeAreaTestEntry");
+ App.WaitForKeyboardToShow();
+ Assert.That(App.IsKeyboardShown(), Is.True, "Keyboard should be visible after tapping entry");
+
+#if !ANDROID // On Android, Appium does not find the bottom label when the keyboard is open
+ // Bottom should NOT move (Default on ContentView = None — ignores keyboard)
+ var bottomLabelDuringRect = App.WaitForElement("BottomEdgeIndicator").GetRect();
+ Assert.That(Math.Abs(bottomLabelDuringRect.Bottom), Is.EqualTo(screenHeight),
+ $"During keyboard - bottom label Bottom ({bottomLabelDuringRect.Bottom}) should equal screenHeight ({screenHeight})");
+#endif
+ // Top should remain unchanged
+ var topLabelDuringRect = App.WaitForElement("TopEdgeIndicator").GetRect();
+ Assert.That(topLabelDuringRect.Y, Is.EqualTo(0),
+ $"During keyboard - top label Y ({topLabelDuringRect.Y}) should remain at 0 (edge-to-edge)");
+
+ App.DismissKeyboard();
+ App.WaitForKeyboardToHide();
+ Assert.That(App.IsKeyboardShown(), Is.False, "Keyboard should be hidden after dismissal");
+
+ var topLabelAfterRect = App.WaitForElement("TopEdgeIndicator").GetRect();
+ Assert.That(topLabelAfterRect.Y, Is.EqualTo(topLabelBeforeRect.Y),
+ $"After keyboard - top label Y ({topLabelAfterRect.Y}) should return to original ({topLabelBeforeRect.Y})");
+
+ var bottomLabelAfterRect = App.WaitForElement("BottomEdgeIndicator").GetRect();
+ Assert.That(bottomLabelAfterRect.Bottom, Is.EqualTo(bottomLabelBeforeRect.Bottom),
+ $"After keyboard - bottom label Bottom ({bottomLabelAfterRect.Bottom}) should return to original ({bottomLabelBeforeRect.Bottom})");
+ }
+
+ // ──────────────────────────────────────────────
+ // Per-Edge + Keyboard (Portrait)
+ // ──────────────────────────────────────────────
+
+ [Test, Order(33)]
+ [Description("Per-edge B:None + keyboard — bottom stays edge-to-edge when keyboard is shown")]
+ public void Validate_ContentView_PerEdgeKeyboard_BottomNone_BottomStays()
+ {
+ ClickContentViewSafeAreaButton();
+ App.DismissKeyboard();
+
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("TopContainer");
+ App.Tap("TopContainer");
+ App.Tap("BottomNone");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+
+ App.WaitForElement("SafeAreaEdgesValueLabel");
+ Assert.That(App.FindElement("SafeAreaEdgesValueLabel").GetText(), Is.EqualTo("L:None, T:Container, R:None, B:None"));
+
+ var (_, screenHeight) = GetScreenSize();
+
+ var bottomLabelBeforeRect = App.WaitForElement("BottomEdgeIndicator").GetRect();
+ Assert.That(Math.Abs(bottomLabelBeforeRect.Bottom), Is.EqualTo(screenHeight),
+ $"Before keyboard - bottom label Bottom ({bottomLabelBeforeRect.Bottom}) should be equal to screenHeight ({screenHeight})");
+
+ Assert.That(App.IsKeyboardShown(), Is.False, "Keyboard should not be visible before tapping entry");
+ App.Tap("SafeAreaTestEntry");
+ App.WaitForKeyboardToShow();
+ Assert.That(App.IsKeyboardShown(), Is.True, "Keyboard should be visible after tapping entry");
+
+#if !ANDROID // On Android, Appium does not find the bottom label when the keyboard is open
+ var bottomLabelDuringRect = App.WaitForElement("BottomEdgeIndicator").GetRect();
+ Assert.That(Math.Abs(bottomLabelDuringRect.Bottom), Is.EqualTo(screenHeight),
+ $"During keyboard - bottom label Bottom ({bottomLabelDuringRect.Bottom}) should stay at screenHeight ({screenHeight})");
+#endif
+ App.DismissKeyboard();
+ App.WaitForKeyboardToHide();
+ Assert.That(App.IsKeyboardShown(), Is.False, "Keyboard should be hidden after dismissal");
+
+ var bottomLabelAfterRect = App.WaitForElement("BottomEdgeIndicator").GetRect();
+ Assert.That(bottomLabelAfterRect.Bottom, Is.EqualTo(bottomLabelBeforeRect.Bottom),
+ $"After keyboard - bottom label Bottom ({bottomLabelAfterRect.Bottom}) should return to original ({bottomLabelBeforeRect.Bottom})");
+ }
+
+ [Test, Order(34)]
+ [Description("Per-edge B:Container + keyboard — bottom stays at safe area inset when keyboard is shown")]
+ public void Validate_ContentView_PerEdgeKeyboard_BottomContainer_BottomStays()
+ {
+ ClickContentViewSafeAreaButton();
+ App.DismissKeyboard();
+
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("TopContainer");
+ App.Tap("TopContainer");
+ App.Tap("BottomContainer");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+
+ App.WaitForElement("SafeAreaEdgesValueLabel");
+ Assert.That(App.FindElement("SafeAreaEdgesValueLabel").GetText(), Is.EqualTo("L:None, T:Container, R:None, B:Container"));
+
+ var insets = GetSafeAreaInsets();
+ var (_, screenHeight) = GetScreenSize();
+
+ var bottomLabelBeforeRect = App.WaitForElement("BottomEdgeIndicator").GetRect();
+ Assert.That(Math.Abs(bottomLabelBeforeRect.Bottom), Is.EqualTo(screenHeight - insets.Bottom),
+ $"Before keyboard - bottom label Bottom ({bottomLabelBeforeRect.Bottom}) should be equal to (screenHeight - insets.Bottom) ({screenHeight - insets.Bottom})");
+
+ Assert.That(App.IsKeyboardShown(), Is.False, "Keyboard should not be visible before tapping entry");
+ App.Tap("SafeAreaTestEntry");
+ App.WaitForKeyboardToShow();
+ Assert.That(App.IsKeyboardShown(), Is.True, "Keyboard should be visible after tapping entry");
+
+#if !ANDROID // On Android, Appium does not find the bottom label when the keyboard is open
+ var bottomLabelDuringRect = App.WaitForElement("BottomEdgeIndicator").GetRect();
+ Assert.That(bottomLabelDuringRect.Bottom, Is.EqualTo(screenHeight - insets.Bottom),
+ $"During keyboard - bottom label Bottom ({bottomLabelDuringRect.Bottom}) should stay at (screenHeight - insets.Bottom) ({screenHeight - insets.Bottom})");
+#endif
+ App.DismissKeyboard();
+ App.WaitForKeyboardToHide();
+ Assert.That(App.IsKeyboardShown(), Is.False, "Keyboard should be hidden after dismissal");
+
+ var bottomLabelAfterRect = App.WaitForElement("BottomEdgeIndicator").GetRect();
+ Assert.That(bottomLabelAfterRect.Bottom, Is.EqualTo(bottomLabelBeforeRect.Bottom),
+ $"After keyboard - bottom label Bottom ({bottomLabelAfterRect.Bottom}) should return to original ({bottomLabelBeforeRect.Bottom})");
+ }
+
+ [Test, Order(35)]
+ [Description("Per-edge B:SoftInput + keyboard — bottom moves up to keyboard Y")]
+ public void Validate_ContentView_PerEdgeKeyboard_BottomSoftInput_BottomMovesUp()
+ {
+ ClickContentViewSafeAreaButton();
+ App.DismissKeyboard();
+
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("TopContainer");
+ App.Tap("TopContainer");
+ App.Tap("BottomSoftInput");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+
+ App.WaitForElement("SafeAreaEdgesValueLabel");
+ Assert.That(App.FindElement("SafeAreaEdgesValueLabel").GetText(), Is.EqualTo("L:None, T:Container, R:None, B:SoftInput"));
+
+ var (_, screenHeight) = GetScreenSize();
+
+ var bottomLabelBeforeRect = App.WaitForElement("BottomEdgeIndicator").GetRect();
+ Assert.That(Math.Abs(bottomLabelBeforeRect.Bottom), Is.EqualTo(screenHeight),
+ $"Before keyboard - bottom label Bottom ({bottomLabelBeforeRect.Bottom}) should be equal to screenHeight ({screenHeight})");
+
+ Assert.That(App.IsKeyboardShown(), Is.False, "Keyboard should not be visible before tapping entry");
+ App.Tap("SafeAreaTestEntry");
+ App.WaitForKeyboardToShow();
+ Assert.That(App.IsKeyboardShown(), Is.True, "Keyboard should be visible after tapping entry");
+
+ var keyboardY = GetKeyboardY();
+
+ var bottomLabelDuringRect = App.WaitForElement("BottomEdgeIndicator").GetRect();
+ Assert.That(bottomLabelDuringRect.Bottom, Is.EqualTo(keyboardY),
+ $"During keyboard - bottom label Bottom ({bottomLabelDuringRect.Bottom}) should equal keyboard Y ({keyboardY})");
+
+ App.DismissKeyboard();
+ App.WaitForKeyboardToHide();
+ Assert.That(App.IsKeyboardShown(), Is.False, "Keyboard should be hidden after dismissal");
+
+ var bottomLabelAfterRect = App.WaitForElement("BottomEdgeIndicator").GetRect();
+ Assert.That(bottomLabelAfterRect.Bottom, Is.EqualTo(bottomLabelBeforeRect.Bottom),
+ $"After keyboard - bottom label Bottom ({bottomLabelAfterRect.Bottom}) should return to original ({bottomLabelBeforeRect.Bottom})");
+ }
+
+ [Test, Order(36)]
+ [Description("Per-edge B:All + keyboard — bottom moves up to keyboard Y")]
+ public void Validate_ContentView_PerEdgeKeyboard_BottomAll_BottomMovesUp()
+ {
+ ClickContentViewSafeAreaButton();
+ App.DismissKeyboard();
+
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("TopContainer");
+ App.Tap("TopContainer");
+ App.Tap("BottomAll");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+
+ App.WaitForElement("SafeAreaEdgesValueLabel");
+ Assert.That(App.FindElement("SafeAreaEdgesValueLabel").GetText(), Is.EqualTo("L:None, T:Container, R:None, B:All"));
+
+ var insets = GetSafeAreaInsets();
+ var (_, screenHeight) = GetScreenSize();
+
+ var bottomLabelBeforeRect = App.WaitForElement("BottomEdgeIndicator").GetRect();
+ Assert.That(Math.Abs(bottomLabelBeforeRect.Bottom), Is.EqualTo(screenHeight - insets.Bottom),
+ $"Before keyboard - bottom label Bottom ({bottomLabelBeforeRect.Bottom}) should be equal to (screenHeight - insets.Bottom) ({screenHeight - insets.Bottom})");
+
+ Assert.That(App.IsKeyboardShown(), Is.False, "Keyboard should not be visible before tapping entry");
+ App.Tap("SafeAreaTestEntry");
+ App.WaitForKeyboardToShow();
+ Assert.That(App.IsKeyboardShown(), Is.True, "Keyboard should be visible after tapping entry");
+
+ var keyboardY = GetKeyboardY();
+
+ var bottomLabelDuringRect = App.WaitForElement("BottomEdgeIndicator").GetRect();
+ Assert.That(bottomLabelDuringRect.Bottom, Is.EqualTo(keyboardY),
+ $"During keyboard - bottom label Bottom ({bottomLabelDuringRect.Bottom}) should equal keyboard Y ({keyboardY})");
+
+ App.DismissKeyboard();
+ App.WaitForKeyboardToHide();
+ Assert.That(App.IsKeyboardShown(), Is.False, "Keyboard should be hidden after dismissal");
+
+ var bottomLabelAfterRect = App.WaitForElement("BottomEdgeIndicator").GetRect();
+ Assert.That(bottomLabelAfterRect.Bottom, Is.EqualTo(bottomLabelBeforeRect.Bottom),
+ $"After keyboard - bottom label Bottom ({bottomLabelAfterRect.Bottom}) should return to original ({bottomLabelBeforeRect.Bottom})");
+ }
+
+ // ──────────────────────────────────────────────
+ // Left/Right Per-Edge in Landscape
+ // ──────────────────────────────────────────────
+
+ [Test, Order(37)]
+ [Description("Landscape per-edge: L:Container, R:None — left inset by safe area, right edge-to-edge")]
+ public void Validate_ContentView_PerEdge_LeftContainerRightNone_Landscape()
+ {
+ ClickContentViewSafeAreaButton();
+ App.DismissKeyboard();
+
+ App.WaitForElement("Options");
+ App.Tap("Options");
+ App.WaitForElement("LeftContainer");
+ App.Tap("LeftContainer");
+ App.Tap("RightNone");
+ App.WaitForElement("Apply");
+ App.Tap("Apply");
+
+ App.WaitForElement("SafeAreaEdgesValueLabel");
+ Assert.That(App.FindElement("SafeAreaEdgesValueLabel").GetText(), Is.EqualTo("L:Container, T:None, R:None, B:None"));
+
+ App.SetOrientationLandscape();
+ Thread.Sleep(1000);
+
+ var (screenWidth, screenHeight) = GetScreenSize();
+ var insetsLandscape = GetSafeAreaInsets();
+
+ // Left: inset by safe area (Container)
+ var leftRect = App.WaitForElement("LeftEdgeIndicator").GetRect();
+ Assert.That(Math.Abs(leftRect.X), Is.EqualTo(insetsLandscape.Left),
+ $"Left (Container): X ({leftRect.X}) should be = insetsLandscape.Left ({insetsLandscape.Left})");
+
+ // Right: edge-to-edge (None)
+ var rightRect = App.WaitForElement("RightEdgeIndicator").GetRect();
+ var rightEdge = rightRect.X + rightRect.Width;
+ Assert.That(Math.Abs(rightEdge), Is.EqualTo(screenWidth),
+ $"Right (None): right edge ({rightEdge}) should be = screenWidth ({screenWidth})");
+
+ App.SetOrientationPortrait();
+ Thread.Sleep(1000);
+ }
+
+ // ──────────────────────────────────────────────
+ // Orientation Roundtrip
+ // ──────────────────────────────────────────────
+
+ [Test, Order(38)]
+ [Description("Rotate to landscape and back to portrait — positions restore correctly")]
+ public void Validate_ContentView_Orientation_Roundtrip_PositionsRestore()
+ {
+ ClickContentViewSafeAreaButton();
+ App.DismissKeyboard();
+
+ App.WaitForElement("SafeAreaAllButton");
+ App.Tap("SafeAreaAllButton");
+ Assert.That(App.FindElement("SafeAreaEdgesValueLabel").GetText(), Is.EqualTo("All"));
+
+ var insets = GetSafeAreaInsets();
+ var (_, screenHeight) = GetScreenSize();
+
+ // ── Record portrait positions ──
+ var topPortraitRect = App.WaitForElement("TopEdgeIndicator").GetRect();
+ var bottomPortraitRect = App.WaitForElement("BottomEdgeIndicator").GetRect();
+
+ Assert.That(Math.Abs(topPortraitRect.Y), Is.EqualTo(insets.Top),
+ $"Portrait: top label Y ({topPortraitRect.Y}) should be equal to insets.Top ({insets.Top})");
+ Assert.That(Math.Abs(bottomPortraitRect.Bottom), Is.EqualTo(screenHeight - insets.Bottom),
+ $"Portrait: bottom label Bottom ({bottomPortraitRect.Bottom}) should be equal to (screenHeight - insets.Bottom) ({screenHeight - insets.Bottom})");
+
+ // ── Rotate to landscape ──
+ App.SetOrientationLandscape();
+ Thread.Sleep(1000);
+
+ var (screenWidthLandscape, screenHeightLandscape) = GetScreenSize();
+ var insetsLandscape = GetSafeAreaInsets();
+
+ var topLandscapeRect = App.WaitForElement("TopEdgeIndicator").GetRect();
+ Assert.That(Math.Abs(topLandscapeRect.Y), Is.EqualTo(insetsLandscape.Top),
+ $"Landscape: top label Y ({topLandscapeRect.Y}) should be equal to insetsLandscape.Top ({insetsLandscape.Top})");
+
+ var leftLandscapeRect = App.WaitForElement("LeftEdgeIndicator").GetRect();
+ Assert.That(Math.Abs(leftLandscapeRect.X), Is.EqualTo(insetsLandscape.Left),
+ $"Landscape: left X ({leftLandscapeRect.X}) should be equal to insetsLandscape.Left ({insetsLandscape.Left})");
+
+ var rightLandscapeRect = App.WaitForElement("RightEdgeIndicator").GetRect();
+ var rightLandscapeEdge = rightLandscapeRect.X + rightLandscapeRect.Width;
+ var expectedRight = GetLandscapeRightInset(insetsLandscape.Right, insetsLandscape.CutoutR);
+ Assert.That(Math.Abs(rightLandscapeEdge), Is.EqualTo(screenWidthLandscape - expectedRight),
+ $"Landscape: right edge ({rightLandscapeEdge}) should be equal to screenWidth - expectedRight ({screenWidthLandscape - expectedRight})");
+
+ // ── Rotate back to portrait ──
+ App.SetOrientationPortrait();
+ Thread.Sleep(1000);
+
+ var insetsAfter = GetSafeAreaInsets();
+ var (_, screenHeightAfter) = GetScreenSize();
+
+ var topAfterRect = App.WaitForElement("TopEdgeIndicator").GetRect();
+ Assert.That(Math.Abs(topAfterRect.Y), Is.EqualTo(insetsAfter.Top),
+ $"After roundtrip: top label Y ({topAfterRect.Y}) should be equal to insets.Top ({insetsAfter.Top})");
+
+ var bottomAfterRect = App.WaitForElement("BottomEdgeIndicator").GetRect();
+ Assert.That(Math.Abs(bottomAfterRect.Bottom), Is.EqualTo(screenHeightAfter - insetsAfter.Bottom),
+ $"After roundtrip: bottom label Bottom ({bottomAfterRect.Bottom}) should be equal to (screenHeight - insets.Bottom) ({screenHeightAfter - insetsAfter.Bottom})");
+
+ // Verify positions match the original portrait positions
+ Assert.That(topAfterRect.Y, Is.EqualTo(topPortraitRect.Y),
+ $"After roundtrip: top label Y ({topAfterRect.Y}) should match original portrait ({topPortraitRect.Y})");
+
+ Assert.That(bottomAfterRect.Bottom, Is.EqualTo(bottomPortraitRect.Bottom),
+ $"After roundtrip: bottom label Bottom ({bottomAfterRect.Bottom}) should match original portrait ({bottomPortraitRect.Bottom})");
+ }
+ }
+}
+#endif
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/SwipeViewFeatureTests.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/SwipeViewFeatureTests.cs
index 4594a4dc7102..40298f5d0476 100644
--- a/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/SwipeViewFeatureTests.cs
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/SwipeViewFeatureTests.cs
@@ -446,7 +446,7 @@ public void VerifySwipeViewWithImageContentSwipeMode()
App.WaitForElement("Apply");
App.Tap("Apply");
App.WaitForElement("SwipeViewImage");
- App.SwipeLeftToRight("SwipeViewImage");
+ App.SwipeLeftToRight("SwipeViewImage", swipePercentage: 0.90);
Assert.That(App.WaitForElement("EventInvokedLabel").GetText(), Is.EqualTo("Label Invoked"));
}
@@ -762,7 +762,7 @@ public void VerifyImageContentWithLabelSwipeItem()
App.WaitForElement("Apply");
App.Tap("Apply");
App.WaitForElement("SwipeViewImage");
- App.SwipeLeftToRight("SwipeViewImage");
+ App.SwipeLeftToRight("SwipeViewImage", swipePercentage: 0.90);
App.WaitForElement("Label");
}
@@ -778,7 +778,7 @@ public void VerifyImageContentWithIconImageSwipeItem()
App.WaitForElement("Apply");
App.Tap("Apply");
App.WaitForElement("SwipeViewImage");
- App.SwipeLeftToRight("SwipeViewImage");
+ App.SwipeLeftToRight("SwipeViewImage", swipePercentage: 0.90);
App.WaitForElement("Icon");
}
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/CollectionViewUITests.CollectionViewItemsUpdatingScrollMode.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/CollectionViewUITests.CollectionViewItemsUpdatingScrollMode.cs
index 09a8d93c8444..54ecddc436c1 100644
--- a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/CollectionViewUITests.CollectionViewItemsUpdatingScrollMode.cs
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/CollectionViewUITests.CollectionViewItemsUpdatingScrollMode.cs
@@ -36,6 +36,10 @@ public void KeepItemsInView()
#if TEST_FAILS_ON_IOS && TEST_FAILS_ON_CATALYST // The test fails on iOS and macOS because Appium is unable to locate the Picker control elements resulting in a TimeoutException. For more information, see: https://github.com/dotnet/maui/issues/28024
// KeepScrollOffset (src\Compatibility\ControlGallery\src\Issues.Shared\CollectionViewItemsUpdatingScrollMode.cs)
+ // After scrolling to the middle, adding items above should not cause the view to scroll.
+ // Previously, the view would automatically scroll to show newly added items, which broke
+ // KeepScrollOffset semantics. With the fix, the visible items remain stable; adding 5 items
+ // above shifts indices so the previously visible "Vegetables.jpg, 10" becomes "FlowerBuds.jpg, 12".
[Test]
[Category(UITestCategories.CollectionView)]
public void KeepScrollOffset()
@@ -47,8 +51,13 @@ public void KeepScrollOffset()
App.WaitForElement("ScrollToMiddle");
App.Click("ScrollToMiddle");
App.WaitForElement("Vegetables.jpg, 10");
- App.Click("AddItemAbove");
- App.WaitForElement("photo.jpg, 9");
+
+ for (int i = 0; i < 5; i++)
+ {
+ App.Click("AddItemAbove");
+ }
+
+ App.WaitForElement("FlowerBuds.jpg, 12");
}
// KeepLastItemInView(src\Compatibility\ControlGallery\src\Issues.Shared\CollectionViewItemsUpdatingScrollMode.cs)
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue13323.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue13323.cs
new file mode 100644
index 000000000000..c51242d082b3
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue13323.cs
@@ -0,0 +1,68 @@
+#if TEST_FAILS_ON_WINDOWS // Related issue: https://github.com/dotnet/maui/issues/29412
+using Microsoft.Maui.TestCases.Tests;
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.AppUITests.Issues;
+
+public class Issue13323 : _IssuesUITest
+{
+ public Issue13323(TestDevice device) : base(device) { }
+
+ public override string Issue => "CarouselView on Android does not work if HorizontalTextAlignment in Entry is not Start";
+
+ [Test]
+ [Category(UITestCategories.CarouselView)]
+ public void CarouselView_EntryTap_DoesNotChangePosition()
+ {
+ NavigateToSecondItem("CarouselView13323", "GoToItem2");
+ App.WaitForTextToBePresentInElement("PositionLabel", "Position:2");
+ App.WaitForElement("CenterEntry_2");
+
+ App.Tap("CenterEntry_2");
+
+ Assert.That(App.FindElement("PositionLabel").GetText(), Is.EqualTo("Position:2"),
+ "CarouselView jumped after tapping Center-aligned Entry.");
+
+ App.DismissKeyboard();
+#if ANDROID
+ App.WaitForKeyboardToHide();
+#endif
+ }
+
+ [Test]
+ [Category(UITestCategories.CarouselView)]
+ public void CarouselView_Loop_EntryTap_DoesNotChangePosition()
+ {
+ NavigateToSecondItem("LoopCarouselView13323", "LoopGoToItem2");
+ App.WaitForTextToBePresentInElement("LoopPositionLabel", "LoopPosition:2");
+ App.WaitForElement("LoopCenterEntry_2");
+
+ App.Tap("LoopCenterEntry_2");
+
+ Assert.That(App.FindElement("LoopPositionLabel").GetText(), Is.EqualTo("LoopPosition:2"),
+ "CarouselView (Loop=true) jumped after tapping Center-aligned Entry.");
+
+ App.DismissKeyboard();
+#if ANDROID
+ App.WaitForKeyboardToHide();
+#endif
+ }
+
+ // Navigates the given CarouselView to item 2. On iOS the classic CarouselView does not reliably
+ // respond to the programmatic ScrollTo in CI, so we drive it with real swipe gestures (the same
+ // approach used by Issue29261). On the other platforms the "Go to Item 2" button is reliable.
+ void NavigateToSecondItem(string carouselId, string goToButtonId)
+ {
+ App.WaitForElement(carouselId);
+#if IOS
+ App.ScrollRight(carouselId, ScrollStrategy.Gesture, 0.9, 500);
+ App.ScrollRight(carouselId, ScrollStrategy.Gesture, 0.9, 500);
+#else
+ App.WaitForElement(goToButtonId);
+ App.Tap(goToButtonId);
+#endif
+ }
+}
+#endif
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue16470.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue16470.cs
deleted file mode 100644
index 523a09cb1bf3..000000000000
--- a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue16470.cs
+++ /dev/null
@@ -1,25 +0,0 @@
-using NUnit.Framework;
-using UITest.Appium;
-using UITest.Core;
-
-namespace Microsoft.Maui.TestCases.Tests.Issues;
-
-public class Issue16470 : _IssuesUITest
-{
- public Issue16470(TestDevice testDevice) : base(testDevice)
- {
- }
-
- public override string Issue => "TabbedPage tab titles are truncated instead of scrolling on Android";
-
- [Test]
- [Category(UITestCategories.TabbedPage)]
- public void TabTitlesShouldNotBeTruncated()
- {
- // Wait for the first tab content to confirm the page loaded
- App.WaitForElement("Tab1Content");
-
- // Verify that the tab bar shows full titles and is scrollable
- VerifyScreenshot();
- }
-}
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue19667.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue19667.cs
new file mode 100644
index 000000000000..c317ccbb3da8
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue19667.cs
@@ -0,0 +1,49 @@
+#if ANDROID || IOS // The test fails on Windows and MacCatalyst because the SetOrientation method, which is intended to change the device orientation, is only supported on mobile platforms iOS and Android.
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues;
+
+public class Issue19667 : _IssuesUITest
+{
+ public Issue19667(TestDevice device) : base(device)
+ {
+ }
+
+ public override string Issue => "CollectionView contents not sizing correctly after orientation change";
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void CollectionViewItemsSizeCorrectlyAfterOrientationChange()
+ {
+ App.TapShellFlyoutIcon();
+ App.Tap("CollectionViewPage");
+ App.WaitForElement("CollectionView19667");
+ App.WaitForElement("CvItem0");
+
+ var portraitWidth = App.WaitForElement("CvItem0").GetRect().Width;
+
+ App.TapShellFlyoutIcon();
+ App.Tap("Page1");
+ App.WaitForElement("Page1Label");
+
+ App.SetOrientationLandscape();
+
+ App.TapShellFlyoutIcon();
+ App.Tap("CollectionViewPage");
+ App.WaitForElement("CollectionView19667");
+ App.WaitForElement("CvItem0");
+
+ var landscapeWidth = App.WaitForElement("CvItem0").GetRect().Width;
+ Assert.That(landscapeWidth, Is.GreaterThan(portraitWidth),
+ "CollectionView items should resize to landscape width after orientation change.");
+ }
+
+ [TearDown]
+ public void TearDown()
+ {
+ App.SetOrientationPortrait();
+ }
+}
+#endif
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue23023.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue23023.cs
new file mode 100644
index 000000000000..02b9d0510877
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue23023.cs
@@ -0,0 +1,35 @@
+#if TEST_FAILS_ON_WINDOWS // Issue Link - https://github.com/dotnet/maui/issues/31670
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues;
+
+public class Issue23023 : _IssuesUITest
+{
+ public Issue23023(TestDevice device) : base(device)
+ {
+ }
+
+ public override string Issue => "CarouselView does not scroll to the specified item at the end after resetting data even though the CurrentItem is updated correctly";
+
+ [Test]
+ [Category(UITestCategories.CarouselView)]
+ public void VerifyCarouselScrollsToEndItemAfterReset()
+ {
+ // iOS 26 changes UICollectionView scroll callback behavior, causing incorrect
+ // CurrentItem/Position during animated ScrollTo — tracked in:
+ // https://github.com/dotnet/maui/issues/34965
+ if (App is AppiumIOSApp iosApp && HelperExtensions.IsIOS26OrHigher(iosApp))
+ {
+ Assert.Ignore("Skipped on iOS 26+ due to carousel scroll behavior change. Issue: https://github.com/dotnet/maui/issues/34965");
+ }
+
+ App.WaitForElement("Issue23023_ReloadItems");
+ App.Tap("Issue23023_ReloadItems");
+ App.Tap("Issue23023_ScrollToLastItem");
+
+ VerifyScreenshot();
+ }
+}
+#endif
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue23074.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue23074.cs
deleted file mode 100644
index 12d837ff390d..000000000000
--- a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue23074.cs
+++ /dev/null
@@ -1,21 +0,0 @@
-#if TEST_FAILS_ON_WINDOWS //The AutomationId for SwipeView items does not function as expected on the Windows platform. Additionally, programmatic swiping is currently not working. For reference: https://github.com/dotnet/maui/issues/14777.
-using NUnit.Framework;
-using UITest.Appium;
-using UITest.Core;
-
-namespace Microsoft.Maui.TestCases.Tests.Issues;
-
-public class Issue23074(TestDevice device) : _IssuesUITest(device)
-{
- public override string Issue => "SwipeItem IconImageSource should allow more configuration";
-
- [Test]
- [Category(UITestCategories.SwipeView)]
- public void SwipeItemFontAndSvgIconsRenderCorrectly()
- {
- App.WaitForElement("SwipeContent");
- App.SwipeRightToLeft("SwipeViewWithIcons");
- VerifyScreenshot();
- }
-}
-#endif
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue23315.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue23315.cs
new file mode 100644
index 000000000000..cb4b373a13d4
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue23315.cs
@@ -0,0 +1,24 @@
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues
+{
+ public class Issue23315(TestDevice device) : _IssuesUITest(device)
+ {
+ public override string Issue => "LoadFile in src/Core/src/Platform/iOS/MauiWKWebView.cs ignore directories";
+
+ [Test]
+ [Category(UITestCategories.WebView)]
+ public void WebViewCanLoadFileFromSubdirectory()
+ {
+ // The HostApp loads `foo/bar/baz/test.html` whose is "Nested Subdirectory Test File".
+ // When the bug is present on iOS/MacCatalyst, LoadFile strips the directory part
+ // and tries to load only `test.html`, so the navigation fails and the label
+ // never reports the expected title.
+ var statusLabel = App.WaitForElement("StatusLabel", timeout: TimeSpan.FromSeconds(10));
+ var text = statusLabel.GetText();
+ Assert.That(text, Is.EqualTo("Success"), $"Expected to load the file from the subdirectory, but got '{text}' instead.");
+ }
+ }
+}
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue24533.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue24533.cs
new file mode 100644
index 000000000000..3d08ff05ee94
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue24533.cs
@@ -0,0 +1,46 @@
+#if TEST_FAILS_ON_WINDOWS && TEST_FAILS_ON_CATALYST
+// TEST_FAILS_ON_WINDOWS : For more info : https://github.com/dotnet/maui/issues/31375
+// TEST_FAILS_ON_CATALYST : ScrollTo is not working properly on MacCatalyst.
+using System.Globalization;
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues
+{
+ public class Issue24533 : _IssuesUITest
+ {
+ public override string Issue => "[iOS] RefreshView causes CollectionView scroll position to reset";
+
+ public Issue24533(TestDevice device) : base(device)
+ {
+ }
+
+ [Test]
+ [Category(UITestCategories.RefreshView)]
+ public void CollectionViewWithRefreshViewShouldNotReset()
+ {
+ App.WaitForElement("Footer");
+ App.Tap("Footer");
+ App.ScrollTo("Footer");
+ App.Tap("Footer");
+ App.ScrollTo("Footer");
+ var verticalOffsetBeforeRefresh = GetVerticalOffset();
+ Assert.That(verticalOffsetBeforeRefresh, Is.GreaterThan(0));
+
+ App.Tap("Footer");
+ App.ScrollTo("Footer");
+ var verticalOffsetAfterRefresh = GetVerticalOffset();
+ Assert.That(verticalOffsetAfterRefresh, Is.GreaterThan(0));
+ }
+
+ double GetVerticalOffset()
+ {
+ var verticalOffsetText = App.WaitForElement("VerticalOffsetLabel").GetText() ?? string.Empty;
+ var verticalOffsetValue = verticalOffsetText.Replace("VerticalOffset:", string.Empty, StringComparison.Ordinal).Trim();
+
+ return double.Parse(verticalOffsetValue, CultureInfo.InvariantCulture);
+ }
+ }
+}
+#endif
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue28064.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue28064.cs
new file mode 100644
index 000000000000..1c6452585800
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue28064.cs
@@ -0,0 +1,34 @@
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues;
+
+public class Issue28064 : _IssuesUITest
+{
+ public Issue28064(TestDevice device) : base(device)
+ {
+ }
+
+ public override string Issue => "TapGestureRecognizer on ScrollView background does not fire on Android";
+
+ [Test]
+ [Category(UITestCategories.ScrollView)]
+ public void ScrollViewBackgroundTapGestureShouldFire()
+ {
+ App.WaitForElement("StatusLabel");
+ App.Tap("TheScrollView");
+ var labelText = App.WaitForElement("StatusLabel").GetText();
+ Assert.That(labelText, Is.EqualTo("ScrollView Tapped"));
+ }
+
+ [Test]
+ [Category(UITestCategories.ScrollView)]
+ public void ScrollViewChildTapGestureShouldFire()
+ {
+ App.WaitForElement("ChildStatusLabel");
+ App.Tap("Child1Label");
+ var labelText = App.WaitForElement("ChildStatusLabel").GetText();
+ Assert.That(labelText, Is.EqualTo("Child Tapped"));
+ }
+}
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue28986_FlyoutPage.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue28986_FlyoutPage.cs
index eaa2401f27b3..e2d373b30c49 100644
--- a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue28986_FlyoutPage.cs
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue28986_FlyoutPage.cs
@@ -21,11 +21,7 @@ public void ToolbarExtendsAllTheWayLeftAndRight_FlyoutPage()
App.WaitForElement("ContentGrid");
App.SetOrientationLandscape();
App.WaitForElement("ContentGrid");
-#if ANDROID
- VerifyScreenshot(cropLeft: 125);
-#else
VerifyScreenshot();
-#endif
}
}
#endif
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue28986_NavigationPage.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue28986_NavigationPage.cs
index ca009469c1fb..69f4894ec45c 100644
--- a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue28986_NavigationPage.cs
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue28986_NavigationPage.cs
@@ -21,11 +21,7 @@ public void ToolbarExtendsAllTheWayLeftAndRight_NavigationPage()
App.WaitForElement("ContentGrid");
App.SetOrientationLandscape();
App.WaitForElement("ContentGrid");
-#if ANDROID
- VerifyScreenshot(cropLeft: 125);
-#else
VerifyScreenshot();
-#endif
}
}
#endif
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue28986_Shell.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue28986_Shell.cs
index 52e672d09ba1..f3f59bcb81c1 100644
--- a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue28986_Shell.cs
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue28986_Shell.cs
@@ -21,11 +21,7 @@ public void ToolbarExtendsAllTheWayLeftAndRight_Shell()
App.WaitForElement("ContentGrid");
App.SetOrientationLandscape();
App.WaitForElement("ContentGrid");
-#if ANDROID
- VerifyScreenshot(cropLeft: 125);
-#else
VerifyScreenshot();
-#endif
}
}
#endif
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue29131.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue29131.cs
new file mode 100644
index 000000000000..6c38ae037ae7
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue29131.cs
@@ -0,0 +1,31 @@
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues;
+
+public class Issue29131 : _IssuesUITest
+{
+ public Issue29131(TestDevice device) : base(device) { }
+
+ public override string Issue => "Android - KeepScrollOffset does not work as expected when new items are added in CollectionView";
+ const string AddNewItem = "AddNewItem";
+ const string ScrollButton = "ScrollButton";
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void KeepScrollOffsetShouldWork()
+ {
+ App.WaitForElement("CollectionView");
+ App.Click("KeepScrollOffsetButton");
+ App.Click(ScrollButton);
+ App.Click(AddNewItem);
+ App.WaitForElement("Item 30");
+ App.Click(ScrollButton);
+ App.Click(AddNewItem);
+ App.WaitForElement("Item 32");
+ App.Click(ScrollButton);
+ App.Click(AddNewItem);
+ App.WaitForElement("Item 30");
+ }
+}
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue29421.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue29421.cs
new file mode 100644
index 000000000000..2207615a1a16
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue29421.cs
@@ -0,0 +1,23 @@
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues;
+
+public class Issue29421 : _IssuesUITest
+{
+ public override string Issue => "KeepScrollOffset Not Working as Expected in CarouselView";
+
+ public Issue29421(TestDevice device)
+ : base(device)
+ { }
+
+ [Test]
+ [Category(UITestCategories.CarouselView)]
+ public void VerifyCarouselViewKeepScrollOffsetAdd()
+ {
+ App.WaitForElement("carouselview");
+ App.Tap("AddButton");
+ VerifyScreenshot();
+ }
+}
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue29898.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue29898.cs
new file mode 100644
index 000000000000..85393fbf3d9e
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue29898.cs
@@ -0,0 +1,32 @@
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues;
+
+public class Issue29898 : _IssuesUITest
+{
+ public override string Issue => "[iOS, macOS] StrokeDashArray on Border does not reset when set to null";
+
+ public Issue29898(TestDevice device)
+ : base(device)
+ { }
+
+ [Test, Order(1)]
+ [Category(UITestCategories.Border)]
+ public void VerifyBorderWithNullStrokeDashArray()
+ {
+ App.WaitForElement("ClearDashButton");
+ App.Tap("ClearDashButton");
+ VerifyScreenshot();
+ }
+
+ [Test, Order(2)]
+ [Category(UITestCategories.Border)]
+ public void VerifyBorderWithStrokeDashArrayValue()
+ {
+ App.WaitForElement("SetDashButton");
+ App.Tap("SetDashButton");
+ VerifyScreenshot();
+ }
+}
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue30081.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue30081.cs
new file mode 100644
index 000000000000..6f15cc182fe1
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue30081.cs
@@ -0,0 +1,22 @@
+#if TEST_FAILS_ON_IOS && TEST_FAILS_ON_CATALYST // More Info: https://github.com/dotnet/maui/issues/32271
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues;
+
+public class Issue30081 : _IssuesUITest
+{
+ public Issue30081(TestDevice device) : base(device) { }
+
+ public override string Issue => "[Android] ScrollView scroll position changes unexpectedly when Orientation is set to Horizontal and FlowDirection is RTL at runtime";
+ [Test]
+ [Category(UITestCategories.ScrollView)]
+ public void VerifyHorizontalScrollViewPositionAtRuntime()
+ {
+ App.WaitForElement("ToggleOrientationButton");
+ App.Tap("ToggleOrientationButton");
+ VerifyScreenshot();
+ }
+}
+#endif
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue30248.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue30248.cs
new file mode 100644
index 000000000000..7813cdad6382
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue30248.cs
@@ -0,0 +1,34 @@
+#if MACCATALYST //This is the Mac Specific issue, so restricting other platforms
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues;
+
+public class Issue30248 : _IssuesUITest
+{
+ public override string Issue => "TitleBar, MacCatalyst - content is not aligned to left on fullscreen";
+
+ public Issue30248(TestDevice device)
+ : base(device)
+ { }
+
+ [Test]
+ [Category(UITestCategories.Window)]
+ public void VerifyTitleBarContentinFullScreenmode()
+ {
+ App.WaitForElement("TitleBarAlignmentLabel");
+ try
+ {
+ App.EnterFullScreen();
+ App.WaitForElement("TitleBarAlignmentLabel");
+ App.Tap("EmptyButton");
+ VerifyScreenshot(includeTitleBar: true);
+ }
+ finally
+ {
+ App.ExitFullScreen();
+ }
+ }
+}
+#endif
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue30515.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue30515.cs
new file mode 100644
index 000000000000..44d90073c08f
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue30515.cs
@@ -0,0 +1,23 @@
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues;
+
+public class Issue30515 : _IssuesUITest
+{
+ public override string Issue => "[iOS] WebView.Reload() with HtmlWebViewSource returns WebNavigationResult.Failure in Navigated event";
+
+ public Issue30515(TestDevice device)
+ : base(device)
+ { }
+
+ [Test]
+ [Category(UITestCategories.WebView)]
+ public void VerifyWebViewHTMLSourceReloadStatus()
+ {
+ App.WaitForElement("NavigationStatusLabel");
+ App.Tap("ReloadButton");
+ Assert.That(App.FindElement("NavigationStatusLabel").GetText(), Is.EqualTo("Success"));
+ }
+}
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue31065.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue31065.cs
new file mode 100644
index 000000000000..454c8e842675
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue31065.cs
@@ -0,0 +1,30 @@
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues;
+
+public class Issue31065 : _IssuesUITest
+{
+ public override string Issue => "IndicatorView square shape does not update on load or dynamically";
+
+ public Issue31065(TestDevice device) : base(device)
+ { }
+
+ [Test, Order(0)]
+ [Category(UITestCategories.IndicatorView)]
+ public void UpdateIndicatorViewSquareShape()
+ {
+ App.WaitForElement("ChangeIndicatorShapeButton");
+ VerifyScreenshot("IndicatorViewSquareShape");
+ }
+
+ [Test, Order(1)]
+ [Category(UITestCategories.IndicatorView)]
+ public void UpdateIndicatorViewCircleShape()
+ {
+ App.Tap("ChangeIndicatorShapeButton");
+ VerifyScreenshot("IndicatorViewCircleShape");
+ }
+
+}
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue32221.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue32221.cs
new file mode 100644
index 000000000000..61eeeee267f3
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue32221.cs
@@ -0,0 +1,20 @@
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues;
+public class Issue32221 : _IssuesUITest
+{
+ public Issue32221(TestDevice device) : base(device) { }
+
+ public override string Issue => "[iOS] ScrollView does not resize when children are removed from StackLayout at runtime";
+ [Test]
+ [Category(UITestCategories.ScrollView)]
+ public void VerifyScrollViewHeightWhenRemoveChildAtRuntime()
+ {
+ App.WaitForElement("AddLabelButton");
+ App.Tap("AddLabelButton");
+ App.Tap("RemoveLabelButton");
+ VerifyScreenshot();
+ }
+}
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue32271.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue32271.cs
new file mode 100644
index 000000000000..0478f9af858c
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue32271.cs
@@ -0,0 +1,20 @@
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues;
+public class Issue32271 : _IssuesUITest
+{
+ public Issue32271(TestDevice device) : base(device) { }
+
+ public override string Issue => "ScrollView with RTL FlowDirection and Horizontal Orientation scrolls in the wrong direction on iOS";
+ [Test]
+ [Category(UITestCategories.ScrollView)]
+ public void VerifyScrollViewDirection()
+ {
+ App.WaitForElement("ToggleOrientationButton");
+ App.Tap("ToggleOrientationButton");
+ App.Tap("ScrollToEndButton");
+ VerifyScreenshot();
+ }
+}
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue32275.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue32275.cs
new file mode 100644
index 000000000000..5205432b3287
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue32275.cs
@@ -0,0 +1,99 @@
+#if ANDROID || IOS // SafeAreaEdges not supported on Catalyst and Windows
+
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues;
+
+public class Issue32275 : _IssuesUITest
+{
+ public override string Issue => "Shell Flyout SafeArea Rendering";
+
+ protected override bool ResetAfterEachTest => true;
+
+ public Issue32275(TestDevice device) : base(device) { }
+
+ // Test 1: Open flyout with default items and capture screenshot
+ [Test, Order(1)]
+ [Category(UITestCategories.SafeAreaEdges)]
+ public void VerifyDefaultFlyoutItemsRendering()
+ {
+ App.WaitForElement("PageLoaded");
+ App.ShowFlyout();
+ VerifyScreenshot();
+ }
+
+ // Test 2: Open flyout with header/footer and capture screenshot
+ [Test, Order(2)]
+ [Category(UITestCategories.SafeAreaEdges)]
+ public void VerifyFlyoutWithHeaderFooter()
+ {
+ App.WaitForElement("ToggleHeaderFooter");
+ App.Tap("ToggleHeaderFooter");
+ App.WaitForElement("PageLoaded");
+ App.ShowFlyout();
+ App.WaitForElement("Header");
+ App.WaitForElement("Footer");
+ VerifyScreenshot();
+ }
+
+ // Test 3: Tap ToggleFlyoutContentTemplate, then open flyout and capture screenshot
+ [Test, Order(3)]
+ [Category(UITestCategories.SafeAreaEdges)]
+ public void VerifyCustomFlyoutContentTemplateRendering()
+ {
+ App.WaitForElement("ToggleFlyoutContentTemplate");
+ App.Tap("ToggleFlyoutContentTemplate");
+ App.WaitForElement("PageLoaded");
+ App.ShowFlyout();
+ VerifyScreenshot();
+ }
+
+ // Test 4: ToggleFlyoutContentTemplate + header/footer, open flyout and capture screenshot
+ [Test, Order(4)]
+ [Category(UITestCategories.SafeAreaEdges)]
+ public void VerifyCustomFlyoutContentTemplateWithHeaderFooter()
+ {
+ App.WaitForElement("ToggleFlyoutContentTemplate");
+ App.Tap("ToggleFlyoutContentTemplate");
+ App.WaitForElement("ToggleHeaderFooter");
+ App.Tap("ToggleHeaderFooter");
+ App.WaitForElement("PageLoaded");
+ App.ShowFlyout();
+ App.WaitForElement("Header");
+ App.WaitForElement("Footer");
+ VerifyScreenshot();
+ }
+
+ // Test 5: Tap Toggle Flyout Content, open flyout, capture screenshot
+ [Test, Order(5)]
+ [Category(UITestCategories.SafeAreaEdges)]
+ public void VerifyCustomFlyoutContentRendering()
+ {
+ App.WaitForElement("ToggleContent");
+ App.Tap("ToggleContent");
+ App.WaitForElement("PageLoaded");
+ App.ShowFlyout();
+ App.WaitForElement("ContentView");
+ VerifyScreenshot();
+ }
+
+ // Test 6: Toggle Flyout Content + header/footer, open flyout, capture screenshot
+ [Test, Order(6)]
+ [Category(UITestCategories.SafeAreaEdges)]
+ public void VerifyCustomFlyoutContentWithHeaderFooter()
+ {
+ App.WaitForElement("ToggleContent");
+ App.Tap("ToggleContent");
+ App.WaitForElement("ToggleHeaderFooter");
+ App.Tap("ToggleHeaderFooter");
+ App.WaitForElement("PageLoaded");
+ App.ShowFlyout();
+ App.WaitForElement("ContentView");
+ App.WaitForElement("Header");
+ App.WaitForElement("Footer");
+ VerifyScreenshot();
+ }
+}
+#endif
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue32435.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue32435.cs
new file mode 100644
index 000000000000..c51fa8d092b1
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue32435.cs
@@ -0,0 +1,24 @@
+#if TEST_FAILS_ON_CATALYST && TEST_FAILS_ON_WINDOWS //Issue reproduce only when rotating device.
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues;
+
+public class Issue32435 : _IssuesUITest
+{
+ public Issue32435(TestDevice device) : base(device) { }
+
+ public override string Issue => "Rotating the Simulator causes the text on the collection view to disappear";
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void VerifyCollectionViewTextShouldAppearAfterRotatingTheDevice()
+ {
+ App.WaitForElement("InstructionLabel");
+ App.Tap("AddButton");
+ App.SetOrientationLandscape();
+ App.SetOrientationPortrait();
+ VerifyScreenshot();
+ }
+}
+#endif
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue32724.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue32724.cs
new file mode 100644
index 000000000000..8e30a7087944
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue32724.cs
@@ -0,0 +1,130 @@
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues;
+
+public class Issue32724 : _IssuesUITest
+{
+ public Issue32724(TestDevice device) : base(device) { }
+
+ public override string Issue => "Applying Shadow property affects the properties in Visual Transform Matrix";
+
+ [Test, Order(1)]
+ [Category(UITestCategories.Border)]
+ public void VerifyScaleAndShadow()
+ {
+ App.WaitForElement("ScaleButton");
+ App.Tap("ScaleButton");
+ App.Tap("ToggleShadowButton");
+ VerifyScreenshot();
+ }
+
+ [Test, Order(2)]
+ [Category(UITestCategories.Border)]
+ public void VerifyScaleXAndShadow()
+ {
+ App.Tap("ResetButton");
+ App.WaitForElement("ScaleXButton");
+ App.Tap("ScaleXButton");
+ App.Tap("ToggleShadowButton");
+ VerifyScreenshot();
+ }
+
+ [Test, Order(3)]
+ [Category(UITestCategories.Border)]
+ public void VerifyScaleYAndShadow()
+ {
+ App.Tap("ResetButton");
+ App.WaitForElement("ScaleYButton");
+ App.Tap("ScaleYButton");
+ App.Tap("ToggleShadowButton");
+ VerifyScreenshot();
+ }
+
+ [Test, Order(4)]
+ [Category(UITestCategories.Border)]
+ public void VerifyTranslationXAndShadow()
+ {
+ App.Tap("ResetButton");
+ App.WaitForElement("TranslationXButton");
+ App.Tap("TranslationXButton");
+ App.Tap("ToggleShadowButton");
+ VerifyScreenshot();
+ }
+
+ [Test, Order(5)]
+ [Category(UITestCategories.Border)]
+ public void VerifyTranslationYAndShadow()
+ {
+ App.Tap("ResetButton");
+ App.WaitForElement("TranslationYButton");
+ App.Tap("TranslationYButton");
+ App.Tap("ToggleShadowButton");
+ VerifyScreenshot();
+ }
+
+ [Test, Order(6)]
+ [Category(UITestCategories.Border)]
+ public void VerifyRotationAndShadow()
+ {
+ App.Tap("ResetButton");
+ App.Tap("RotationButton");
+ App.Tap("ToggleShadowButton");
+ VerifyScreenshot();
+ }
+
+ [Test, Order(7)]
+ [Category(UITestCategories.Border)]
+ public void VerifyRotationXAndShadow()
+ {
+ App.Tap("ResetButton");
+ App.Tap("RotationXButton");
+ App.Tap("ToggleShadowButton");
+ VerifyScreenshot();
+ }
+
+ [Test, Order(8)]
+ [Category(UITestCategories.Border)]
+ public void VerifyRotationYAndShadow()
+ {
+ App.Tap("ResetButton");
+ App.Tap("RotationYButton");
+ App.Tap("ToggleShadowButton");
+ VerifyScreenshot();
+ }
+
+ [Test, Order(9)]
+ [Category(UITestCategories.Border)]
+ public void VerifyAnchorXAndShadow()
+ {
+ App.Tap("ResetButton");
+ App.Tap("AnchorXButton");
+ App.Tap("RotationButton");
+ App.Tap("ToggleShadowButton");
+ VerifyScreenshot();
+ }
+ [Test, Order(10)]
+ [Category(UITestCategories.Border)]
+ public void VerifyAnchorYAndShadow()
+ {
+ App.Tap("ResetButton");
+ App.Tap("AnchorYButton");
+ App.Tap("RotationButton");
+ App.Tap("ToggleShadowButton");
+ VerifyScreenshot();
+ }
+
+ [Test, Order(11)]
+ [Category(UITestCategories.Border)]
+ public void VerifyAnchorXAndAnchorYShadow()
+ {
+ App.Tap("ResetButton");
+ App.Tap("AnchorXButton");
+ App.Tap("AnchorYButton");
+ App.Tap("RotationButton");
+ App.Tap("ToggleShadowButton");
+ VerifyScreenshot();
+ }
+}
+
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue32731.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue32731.cs
deleted file mode 100644
index ed25a856f96b..000000000000
--- a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue32731.cs
+++ /dev/null
@@ -1,132 +0,0 @@
-#if TEST_FAILS_ON_IOS && TEST_FAILS_ON_WINDOWS && TEST_FAILS_ON_CATALYST //More Info: https://github.com/dotnet/maui/issues/32731
-using NUnit.Framework;
-using UITest.Appium;
-using UITest.Core;
-
-namespace Microsoft.Maui.TestCases.Tests.Issues;
-
-public class Issue32731 : _IssuesUITest
-{
- public Issue32731(TestDevice device) : base(device) { }
-
- public override string Issue => "Applying Shadow property affects the properties in Visual Transform Matrix";
-
- [Test, Order(1)]
- [Category(UITestCategories.Border)]
- public void VerifyScaleAndShadow()
- {
- App.WaitForElement("ScaleButton");
- App.Tap("ScaleButton");
- App.Tap("ToggleShadowButton");
- VerifyScreenshot();
- }
-
- [Test, Order(2)]
- [Category(UITestCategories.Border)]
- public void VerifyScaleXAndShadow()
- {
- App.Tap("ResetButton");
- App.WaitForElement("ScaleXButton");
- App.Tap("ScaleXButton");
- App.Tap("ToggleShadowButton");
- VerifyScreenshot();
- }
-
- [Test, Order(3)]
- [Category(UITestCategories.Border)]
- public void VerifyScaleYAndShadow()
- {
- App.Tap("ResetButton");
- App.WaitForElement("ScaleYButton");
- App.Tap("ScaleYButton");
- App.Tap("ToggleShadowButton");
- VerifyScreenshot();
- }
-
- [Test, Order(4)]
- [Category(UITestCategories.Border)]
- public void VerifyTranslationXAndShadow()
- {
- App.Tap("ResetButton");
- App.WaitForElement("TranslationXButton");
- App.Tap("TranslationXButton");
- App.Tap("ToggleShadowButton");
- VerifyScreenshot();
- }
-
- [Test, Order(5)]
- [Category(UITestCategories.Border)]
- public void VerifyTranslationYAndShadow()
- {
- App.Tap("ResetButton");
- App.WaitForElement("TranslationYButton");
- App.Tap("TranslationYButton");
- App.Tap("ToggleShadowButton");
- VerifyScreenshot();
- }
-
- [Test, Order(6)]
- [Category(UITestCategories.Border)]
- public void VerifyRotationAndShadow()
- {
- App.Tap("ResetButton");
- App.Tap("RotationButton");
- App.Tap("ToggleShadowButton");
- VerifyScreenshot();
- }
-
- [Test, Order(7)]
- [Category(UITestCategories.Border)]
- public void VerifyRotationXAndShadow()
- {
- App.Tap("ResetButton");
- App.Tap("RotationXButton");
- App.Tap("ToggleShadowButton");
- VerifyScreenshot();
- }
-
- [Test, Order(8)]
- [Category(UITestCategories.Border)]
- public void VerifyRotationYAndShadow()
- {
- App.Tap("ResetButton");
- App.Tap("RotationYButton");
- App.Tap("ToggleShadowButton");
- VerifyScreenshot();
- }
-
- [Test, Order(9)]
- [Category(UITestCategories.Border)]
- public void VerifyAnchorXAndShadow()
- {
- App.Tap("ResetButton");
- App.Tap("AnchorXButton");
- App.Tap("RotationButton");
- App.Tap("ToggleShadowButton");
- VerifyScreenshot();
- }
-
- [Test, Order(10)]
- [Category(UITestCategories.Border)]
- public void VerifyAnchorYAndShadow()
- {
- App.Tap("ResetButton");
- App.Tap("AnchorYButton");
- App.Tap("RotationButton");
- App.Tap("ToggleShadowButton");
- VerifyScreenshot();
- }
-
- [Test, Order(11)]
- [Category(UITestCategories.Border)]
- public void VerifyAnchorXAndAnchorYShadow()
- {
- App.Tap("ResetButton");
- App.Tap("AnchorXButton");
- App.Tap("AnchorYButton");
- App.Tap("RotationButton");
- App.Tap("ToggleShadowButton");
- VerifyScreenshot();
- }
-}
-#endif
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue33038.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue33038.cs
index 867fec096bd9..cc1bb05892c2 100644
--- a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue33038.cs
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue33038.cs
@@ -19,7 +19,9 @@ public void LayoutShouldBeCorrectOnFirstNavigation()
App.WaitForElement("StartPageLabel");
App.Tap("GoToSignInButton");
App.WaitForElement("SignInLabel");
- VerifyScreenshot();
+ // The layout can take an extra frame to settle after navigation, so retry the screenshot
+ // comparison and allow a small tolerance for cross-machine rendering variance.
+ VerifyScreenshot(tolerance: 0.5, retryTimeout: TimeSpan.FromSeconds(2));
}
}
#endif
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue33110.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue33110.cs
new file mode 100644
index 000000000000..012bb67006dc
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue33110.cs
@@ -0,0 +1,23 @@
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues;
+
+public class Issue33110 : _IssuesUITest
+{
+ public Issue33110(TestDevice device) : base(device)
+ {
+ }
+
+ public override string Issue => "GraphicsView dirtyRect dimensions should be integers, not fractional values";
+
+ [Test]
+ [Category(UITestCategories.GraphicsView)]
+ public void GraphicsViewDirtyRectShouldHaveIntegerDimensions()
+ {
+ App.WaitForElement("CheckButton");
+ App.Tap("CheckButton");
+ Assert.That(App.WaitForElement("ResultLabel").GetText(), Is.EqualTo("Pass"));
+ }
+}
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue33307.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue33307.cs
new file mode 100644
index 000000000000..2702b041f0ff
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue33307.cs
@@ -0,0 +1,37 @@
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues;
+public class Issue33307 : _IssuesUITest
+{
+ public Issue33307(TestDevice device) : base(device) { }
+
+ public override string Issue => "The Picker is still binding to the property and reacts to data changes after the page is closed.";
+ [Test]
+ [Category(UITestCategories.Picker)]
+ public void VerifyPickerItemsinNavigation()
+ {
+ App.WaitForElement("Page1");
+ App.Tap("Page1");
+ App.WaitForElement("AddItems");
+ App.Tap("AddItems");
+ App.TapBackArrow();
+ App.WaitForElement("Page2");
+ App.Tap("Page2");
+ App.WaitForElement("Add");
+ App.Tap("Add");
+ App.WaitForElement("SelectSecondItem");
+ App.Tap("SelectSecondItem");
+ App.TapBackArrow();
+ App.WaitForElement("Page1");
+ App.Tap("Page1");
+ App.WaitForElement("DeleteItem");
+ App.Tap("DeleteItem");
+ App.TapBackArrow();
+ App.WaitForElement("Page2");
+ App.Tap("Page2");
+ App.WaitForElement("StatusLabel");
+ Assert.That(App.FindElement("StatusLabel").GetText(), Is.EqualTo("None"));
+ }
+}
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue33785.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue33785.cs
new file mode 100644
index 000000000000..21694baecd46
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue33785.cs
@@ -0,0 +1,23 @@
+#if WINDOWS //CollapsedPaneWidth is Windows Specific API
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues;
+public class Issue33785 : _IssuesUITest
+{
+ public Issue33785(TestDevice device) : base(device) { }
+
+ public override string Issue => "[Windows] FlyoutPage CollapsedPaneWidth Not Working";
+ [Test]
+ [Category(UITestCategories.FlyoutPage)]
+ public void VerifyFlyoutPageCollapsedPaneWidth()
+ {
+ App.WaitForElement("CollapsedPaneLabel");
+ App.TapFlyoutPageIcon();
+ App.Tap("FlyoutItem");
+ App.TapFlyoutPageIcon();
+ VerifyScreenshot();
+ }
+}
+#endif
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue34257.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue34257.cs
deleted file mode 100644
index 5b77fd9ebf86..000000000000
--- a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue34257.cs
+++ /dev/null
@@ -1,39 +0,0 @@
-#if TEST_FAILS_ON_IOS && TEST_FAILS_ON_CATALYST && TEST_FAILS_ON_WINDOWS //In windows, related issue: https://github.com/dotnet/maui/issues/4715
-using NUnit.Framework;
-using UITest.Appium;
-using UITest.Core;
-
-namespace Microsoft.Maui.TestCases.Tests.Issues;
-
-public class Issue34257 : _IssuesUITest
-{
- public Issue34257(TestDevice device)
- : base(device)
- {
- }
-
- public override string Issue => "CollectionView vertical grid item spacing updates all rows and columns";
-
- [Test]
- [Category(UITestCategories.CollectionView)]
- public void UpdatingHorizontalSpacingShouldResizeBothColumns()
- {
- var firstColumnBefore = App.WaitForElement("FirstColumnTopItem").GetRect();
- App.Tap("ApplyHorizontalSpacingButton");
- App.WaitForElement("StatusLabel", "Spacing=0,80");
- var firstColumnAfter = App.WaitForElement("FirstColumnTopItem").GetRect();
- Assert.That(firstColumnBefore.X, Is.Not.EqualTo(firstColumnAfter.X), $"Expected the first column to move");
- }
-
- [Test]
- [Category(UITestCategories.CollectionView)]
- public void UpdatingVerticalSpacingShouldResizeBothRows()
- {
- var firstColumnBefore = App.WaitForElement("FirstColumnBottomItem").GetRect();
- App.Tap("ApplyVerticalSpacingButton");
- App.WaitForElement("StatusLabel", "Spacing=40,0");
- var firstColumnAfter = App.WaitForElement("FirstColumnBottomItem").GetRect();
- Assert.That(firstColumnBefore.Y, Is.Not.EqualTo(firstColumnAfter.Y), $"Expected the second row to move");
- }
-}
-#endif
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue34318.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue34318.cs
new file mode 100644
index 000000000000..57085d0268f5
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue34318.cs
@@ -0,0 +1,37 @@
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues;
+
+public class Issue34318 : _IssuesUITest
+{
+ public Issue34318(TestDevice device) : base(device) { }
+
+ public override string Issue => "Shell Navigating event should fire on ShellContent change";
+
+ [Test]
+ [Category(UITestCategories.Shell)]
+ public void NavigatingFiresWhenShellContentChanges()
+ {
+ App.WaitForElement("ChangeContentButton");
+
+ App.WaitForElement("ResultLabelA");
+
+ var initialText = App.FindElement("ResultLabelA").GetText() ?? string.Empty;
+ Assert.That(initialText, Is.EqualTo("Waiting"));
+
+ App.Tap("ChangeContentButton");
+
+ App.WaitForElement("PageBLabel");
+
+ var result = App.WaitForTextToBePresentInElement("ResultLabelB", "Navigating");
+
+ Assert.That(result, Is.True, "Navigating event should have fired and updated the label text");
+
+ var countText = App.FindElement("NavigatingCountLabel").GetText() ?? string.Empty;
+
+ Assert.That(countText, Is.EqualTo("1"),
+ "Navigating event should fire exactly once, not multiple times");
+ }
+}
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue34422.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue34422.cs
new file mode 100644
index 000000000000..92717093d5f9
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue34422.cs
@@ -0,0 +1,42 @@
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues;
+
+public class Issue34422 : _IssuesUITest
+{
+ public Issue34422(TestDevice device) : base(device) { }
+
+ public override string Issue => "SearchBar clear button still appears on MacCatalyst after clearing input";
+
+ [Test, Order(1)]
+ [Category(UITestCategories.SearchBar)]
+ public void SearchBarClearButtonShouldBeVisibleWithText()
+ {
+ App.WaitForElement("TestSearchBar");
+ App.Tap("TestSearchBar");
+ App.Tap("AddTextButton");
+#if IOS
+ VerifyScreenshot(cropBottom:1000);
+#else
+ VerifyScreenshot();
+#endif
+ }
+
+ [Test, Order(2)]
+ [Category(UITestCategories.SearchBar)]
+ public void SearchBarClearButtonShouldDisappearAfterClearingInput()
+ {
+ // First add text so the clear button appears
+ App.WaitForElement("TestSearchBar");
+ App.Tap("TestSearchBar");
+ App.Tap("AddTextButton");
+ App.Tap("ClearButton");
+#if IOS
+ VerifyScreenshot(cropBottom:1000);
+#else
+ VerifyScreenshot();
+#endif
+ }
+}
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue34666.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue34666.cs
index d6c05c58d11f..a558e38478bd 100644
--- a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue34666.cs
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue34666.cs
@@ -1,4 +1,3 @@
-#if TEST_FAILS_ON_WINDOWS // The issue also affects Windows; tracked for follow-up in: https://github.com/dotnet/maui/issues/34701
using NUnit.Framework;
using UITest.Appium;
using UITest.Core;
@@ -11,16 +10,15 @@ public Issue34666(TestDevice device) : base(device)
{
}
- public override string Issue => "The C6 page cannot scroll on Windows and Android platforms";
+ public override string Issue => "Disabling RefreshView cascades IsEnabled=false to its child CollectionView, preventing scrolling";
[Test]
[Category(UITestCategories.CollectionView)]
- public void CollectionViewScrollsWhenRefreshViewDisabled()
+ public void CollectionViewDoesNotScrollWhenRefreshViewDisabled()
{
App.WaitForElement("Baboon");
App.ScrollDown("CollectionView");
App.ScrollDown("CollectionView");
- App.WaitForElement("Gelada");
+ App.WaitForElement("Baboon");
}
-}
-#endif
\ No newline at end of file
+}
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue34931.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue34931.cs
new file mode 100644
index 000000000000..13c7a00d2c2d
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue34931.cs
@@ -0,0 +1,41 @@
+#if TEST_FAILS_ON_WINDOWS // On Windows, Shell custom flyout-item taps are not working in this scenario, so this issue test is excluded.
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues;
+
+public class Issue34931 : _IssuesUITest
+{
+ public Issue34931(TestDevice device)
+ : base(device)
+ {
+ }
+
+ public override string Issue => "Shell flyout item template does not update selected visuals after DynamicResource changes";
+
+ [Test]
+ [Category(UITestCategories.Shell)]
+ public void FlyoutSelectedStateReflectsUpdatedDynamicResource()
+ {
+ App.WaitForElement("ChangeColorButton");
+ App.Tap("ChangeColorButton");
+ Assert.That(App.WaitForElement("CurrentColorLabel").GetText(), Does.Contain("#FF6347"));
+ NavigateWithFlyout("Second", "Issue34931SecondPageLabel");
+ NavigateWithFlyout("Third", "Issue34931ThirdPageLabel");
+ NavigateWithFlyout("Home", "ChangeColorButton");
+ App.Tap("ChangeColorButton");
+ App.TapShellFlyoutIcon();
+ App.WaitForElement("Third");
+ VerifyScreenshot();
+ }
+
+ void NavigateWithFlyout(string flyoutItemTitle, string pageReadyElement)
+ {
+ App.TapShellFlyoutIcon();
+ App.WaitForElement(flyoutItemTitle);
+ App.Tap(flyoutItemTitle);
+ App.WaitForElement(pageReadyElement);
+ }
+}
+#endif
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue35216.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue35216.cs
new file mode 100644
index 000000000000..5e4d6842fc1d
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue35216.cs
@@ -0,0 +1,75 @@
+#if WINDOWS // Existing PR for iOS & Android: https://github.com/dotnet/maui/pull/35217
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues;
+
+public class Issue35216 : _IssuesUITest
+{
+ public override string Issue => "SwipeItem IsVisible should properly refresh native swipe items when binding value changes dynamically";
+
+ public Issue35216(TestDevice device) : base(device)
+ {
+ }
+
+ [Test]
+ [Order(1)]
+ [Category(UITestCategories.SwipeView)]
+ public void Issue35216SwipeItemInitiallyHiddenBecomesVisibleAfterBindingChanges()
+ {
+ Exception? exception = null;
+ var rect = App.WaitForElement("SwipeContent").GetRect();
+ var centerX = rect.X + rect.Width / 2;
+ var centerY = rect.Y + rect.Height / 2;
+
+ App.DragCoordinates(centerX, centerY, centerX + 200, centerY);
+
+ VerifyScreenshotOrSetException(ref exception, "Issue35216SwipeOpen_InitiallyHidden",
+ retryTimeout: TimeSpan.FromSeconds(2), tolerance: 1.0);
+
+ App.Tap("ToggleVisibilityButton");
+
+ App.DragCoordinates(centerX, centerY, centerX + 200, centerY);
+ VerifyScreenshotOrSetException(ref exception, "Issue35216SwipeOpen_BecomeVisible",
+ retryTimeout: TimeSpan.FromSeconds(2), tolerance: 1.0);
+
+ App.Tap("ResetButton");
+
+ if (exception is not null)
+ {
+ throw exception;
+ }
+ }
+
+ [Test]
+ [Order(2)]
+ [Category(UITestCategories.SwipeView)]
+ public void Issue35216SwipeItemBecomesHiddenAfterBindingChanges()
+ {
+ Exception? exception = null;
+ var rect = App.WaitForElement("SwipeContent").GetRect();
+ var centerX = rect.X + rect.Width / 2;
+ var centerY = rect.Y + rect.Height / 2;
+
+ App.Tap("ToggleVisibilityButton");
+ App.DragCoordinates(centerX, centerY, centerX + 200, centerY);
+
+ VerifyScreenshotOrSetException(ref exception, "Issue35216SwipeOpen_DeleteVisible",
+ retryTimeout: TimeSpan.FromSeconds(2), tolerance: 1.0);
+
+ App.Tap("ToggleVisibilityButton");
+
+ App.DragCoordinates(centerX, centerY, centerX + 200, centerY);
+ VerifyScreenshotOrSetException(ref exception, "Issue35216SwipeOpen_DeleteHidden",
+ retryTimeout: TimeSpan.FromSeconds(2), tolerance: 1.0);
+
+ App.Tap("ResetButton");
+
+ if (exception is not null)
+ {
+ throw exception;
+ }
+ }
+}
+#endif
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue35386.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue35386.cs
new file mode 100644
index 000000000000..db6fe6631732
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue35386.cs
@@ -0,0 +1,25 @@
+#if IOS || ANDROID // SoftAreaEdges is only available on mobile platforms
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues;
+
+public class Issue35386 : _IssuesUITest
+{
+ public Issue35386(TestDevice device) : base(device) { }
+
+ public override string Issue => "MauiView leaks detached platform views when SafeAreaEdges includes SoftInput";
+
+ [Test]
+ [Category(UITestCategories.SafeAreaEdges)]
+ public void SoftInputSafeArea_DetachedPlatformViews_DoNotLeak()
+ {
+ App.WaitForElement("statusLabel");
+ Assert.That(
+ App.WaitForTextToBePresentInElement("statusLabel", "Suspect SafeAreaEdges.SoftInput: virtual=0/12, handler=0/12, platform=0/12", timeout: TimeSpan.FromSeconds(60)),
+ Is.True,
+ "The status label did not reach the expected SoftInput summary text.");
+ }
+}
+#endif
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue35471.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue35471.cs
new file mode 100644
index 000000000000..a02850495562
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue35471.cs
@@ -0,0 +1,45 @@
+// iOS/MacCatalyst only: The fix updates UINavigationItem.Title for back-stack pages,
+// which iOS uses to display the back button text. This is an Apple platform-specific behavior.
+#if IOS || MACCATALYST
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues
+{
+ public class Issue35471 : _IssuesUITest
+ {
+ public override string Issue => "iOS Shell back button history menu does not update after runtime culture change";
+
+ public Issue35471(TestDevice device) : base(device)
+ {
+ }
+
+ [Test]
+ [Category(UITestCategories.Shell)]
+ public void ShellBackButtonHistoryUpdatesAfterTitleChange()
+ {
+ // iOS 26+ no longer exposes the back button title text via accessibility,
+ // so there is no reliable way to assert the updated title in a UI test.
+ if (App is AppiumIOSApp iosApp && HelperExtensions.IsIOS26OrHigher(iosApp))
+ {
+ Assert.Ignore("iOS 26+ does not expose back button title text via accessibility");
+ }
+
+ // Navigate to Detail page
+ App.WaitForElement("NavigateToDetail");
+ App.Tap("NavigateToDetail");
+
+ // Change the previous page's title (simulates runtime culture change)
+ App.WaitForElement("ChangePreviousPageTitle");
+ App.Tap("ChangePreviousPageTitle");
+
+ // Verify back button now shows updated title "Accueil"
+ // Without the fix, UINavigationItem.Title is stale and still shows "Home"
+ App.WaitForElement("Accueil");
+ App.Tap("Accueil");
+ App.WaitForElement("RootPageLabel");
+ }
+ }
+}
+#endif
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue35490.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue35490.cs
new file mode 100644
index 000000000000..df686a039a2f
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue35490.cs
@@ -0,0 +1,24 @@
+#if IOS || MACCATALYST // The floating glass tab bar is a UIKit-only feature introduced in iOS/MacCatalyst 26. This issue does not affect Android or Windows.
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues;
+
+public class Issue35490 : _IssuesUITest
+{
+ public Issue35490(TestDevice testDevice) : base(testDevice)
+ {
+ }
+
+ public override string Issue => "[iOS 26] TabbedPage with NavigationPage children clips content above floating glass tab bar";
+
+ [Test]
+ [Category(UITestCategories.TabbedPage)]
+ public void NavigationPageChildContentExtendsUnderFloatingTabBar()
+ {
+ App.WaitForElement("Tab1Label");
+ VerifyScreenshot();
+ }
+}
+#endif
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue35613.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue35613.cs
new file mode 100644
index 000000000000..6ecde7abbb03
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue35613.cs
@@ -0,0 +1,62 @@
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues;
+
+public class Issue35613 : _IssuesUITest
+{
+ public override string Issue => "OnNavigatingFrom with a NavigationPage always has an incorrect DestinationPage parameter";
+
+ public Issue35613(TestDevice device) : base(device)
+ {
+ }
+
+ [Test]
+ [Category(UITestCategories.Navigation)]
+ public void VerifyNavigatingToAndNavigatingFromArgsForPopAndPopToRoot()
+ {
+ // First page is shown — navigate to second
+ App.WaitForElement("Issue35613_NavigateButton");
+ App.Tap("Issue35613_NavigateButton");
+
+ // Second page: verify Push events from First→Second
+ App.WaitForElement("Issue35613_Second_LogEditor");
+ var secondLog = App.FindElement("Issue35613_Second_LogEditor").GetText();
+ Assert.That(secondLog, Does.Contain("OnNavigatingFrom FirstPage [Push], DestinationPage=Issue35613SecondPage"));
+ Assert.That(secondLog, Does.Contain("OnNavigatedFrom FirstPage [Push], DestinationPage=Issue35613SecondPage"));
+ Assert.That(secondLog, Does.Contain("OnNavigatedTo SecondPage [Push], PreviousPage=Issue35613FirstPage"));
+
+ // Navigate to third
+ App.Tap("Issue35613_NavigateToThirdButton");
+
+ // Third page: verify Push events from Second→Third
+ App.WaitForElement("Issue35613_Third_LogEditor");
+ var thirdLog = App.FindElement("Issue35613_Third_LogEditor").GetText();
+ Assert.That(thirdLog, Does.Contain("OnNavigatingFrom SecondPage [Push], DestinationPage=Issue35613ThirdPage"));
+ Assert.That(thirdLog, Does.Contain("OnNavigatedFrom SecondPage [Push], DestinationPage=Issue35613ThirdPage"));
+ Assert.That(thirdLog, Does.Contain("OnNavigatedTo ThirdPage [Push], PreviousPage=Issue35613SecondPage"));
+
+ // PopToRoot back to first
+ App.Tap("Issue35613_PopToRootButton");
+
+ // First page: verify PopToRoot events
+ App.WaitForElement("Issue35613_LogEditor");
+ var firstLogAfterPopToRoot = App.FindElement("Issue35613_LogEditor").GetText();
+ Assert.That(firstLogAfterPopToRoot, Does.Contain("OnNavigatingFrom ThirdPage [PopToRoot], DestinationPage=Issue35613FirstPage"));
+ Assert.That(firstLogAfterPopToRoot, Does.Contain("OnNavigatedFrom ThirdPage [PopToRoot], DestinationPage=Issue35613FirstPage"));
+ Assert.That(firstLogAfterPopToRoot, Does.Contain("OnNavigatedTo FirstPage [PopToRoot], PreviousPage=Issue35613ThirdPage"));
+
+ // Navigate to second again, then pop back
+ App.Tap("Issue35613_NavigateButton");
+ App.WaitForElement("Issue35613_Second_LogEditor");
+ App.Tap("Issue35613_NavigateBackButton");
+
+ // First page: verify Pop events from Second→First
+ App.WaitForElement("Issue35613_LogEditor");
+ var firstLogAfterPop = App.FindElement("Issue35613_LogEditor").GetText();
+ Assert.That(firstLogAfterPop, Does.Contain("OnNavigatingFrom SecondPage [Pop], DestinationPage=Issue35613FirstPage"));
+ Assert.That(firstLogAfterPop, Does.Contain("OnNavigatedFrom SecondPage [Pop], DestinationPage=Issue35613FirstPage"));
+ Assert.That(firstLogAfterPop, Does.Contain("OnNavigatedTo FirstPage [Pop], PreviousPage=Issue35613SecondPage"));
+ }
+}
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue35675.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue35675.cs
new file mode 100644
index 000000000000..b43f6b35fc81
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue35675.cs
@@ -0,0 +1,27 @@
+#if TEST_FAILS_ON_ANDROID && TEST_FAILS_ON_WINDOWS // Android Issue: https://github.com/dotnet/maui/issues/35643, Windows PR: https://github.com/dotnet/maui/pull/35398
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues;
+
+public class Issue35675 : _IssuesUITest
+{
+ public Issue35675(TestDevice device) : base(device)
+ {
+ }
+
+ public override string Issue => "[iOS] CarouselView freezes with infinite loop when IsScrollAnimated=False";
+
+ [Test]
+ [Category(UITestCategories.CarouselView)]
+ public void CV2DoesNotFreezeWhenSettingCurrentItemWithIsScrollAnimatedFalse()
+ {
+ App.WaitForElement("ScrollButton");
+ App.WaitForElement("Item 0");
+ App.Tap("ScrollButton");
+
+ App.WaitForElement("Item 2b");
+ }
+}
+#endif
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue35700.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue35700.cs
new file mode 100644
index 000000000000..fed51bb29d5e
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue35700.cs
@@ -0,0 +1,22 @@
+#if TEST_FAILS_ON_CATALYST && TEST_FAILS_ON_IOS && TEST_FAILS_ON_WINDOWS // Related issue: https://github.com/dotnet/maui/issues/36545
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues;
+
+public class Issue35700 : _IssuesUITest
+{
+ public Issue35700(TestDevice device) : base(device) { }
+
+ public override string Issue => "Grouped CollectionView items not rendered properly on Android with GridItemsLayout";
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void GroupedCollectionViewGridLayoutRendersCorrectly()
+ {
+ App.WaitForElement("TestCollectionView");
+ VerifyScreenshot();
+ }
+}
+#endif
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue35736.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue35736.cs
new file mode 100644
index 000000000000..bdd886ccaae9
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue35736.cs
@@ -0,0 +1,91 @@
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues;
+
+public class Issue35736 : _IssuesUITest
+{
+ public Issue35736(TestDevice device) : base(device) { }
+
+ public override string Issue => "SearchHandler QueryIcon, ClearIcon, ClearPlaceholderIcon need to update visually at runtime";
+
+ [Test]
+ [Category(UITestCategories.Shell)]
+ public void SearchHandlerQueryIconUpdatesAtRuntime()
+ {
+ App.WaitForElement("Issue35736QueryIconLabel");
+
+ App.Tap("Issue35736ToggleQueryIcon");
+ App.WaitForElement("Issue35736QueryIconLabel");
+
+#if IOS
+ VerifyScreenshot(cropBottom:1000);
+#else
+ VerifyScreenshot();
+#endif
+ }
+
+ [Test]
+ [Category(UITestCategories.Shell)]
+#if WINDOWS
+ [Ignore("ClearPlaceholderIcon is not displayed in Shell SearchHander : https://github.com/dotnet/maui/issues/28619")]
+#endif
+ public void SearchHandlerClearPlaceholderIconUpdatesAtRuntime()
+ {
+ App.WaitForElement("Issue35736ClearPlaceholderIconLabel");
+
+ App.Tap("Issue35736ToggleClearPlaceholderIcon");
+ App.WaitForElement("Issue35736ClearPlaceholderIconLabel");
+
+#if IOS
+ VerifyScreenshot(cropBottom:1000);
+#else
+ VerifyScreenshot();
+#endif
+ }
+
+ [Test]
+ [Category(UITestCategories.Shell)]
+#if WINDOWS
+ [Ignore("ClearIcon is not displayed in Shell SearchHander : https://github.com/dotnet/maui/issues/28619")]
+#endif
+ public void SearchHandlerClearIconUpdatesAtRuntime()
+ {
+ App.WaitForElement("Issue35736ClearIconLabel");
+
+ App.Tap("Issue35736ToggleClearIcon");
+ // Type text so the clear (X) button becomes visible
+ App.EnterTextInShellSearchHandler("A");
+
+ App.WaitForElement("Issue35736ClearIconLabel");
+
+#if IOS
+ VerifyScreenshot(cropBottom:1000);
+#else
+ VerifyScreenshot();
+#endif
+ }
+
+ [Test]
+ [Category(UITestCategories.Shell)]
+ public void SearchHandlerResetAllRestoresDefaultIcons()
+ {
+ App.WaitForElement("Issue35736QueryIconLabel");
+
+ // Change all icons first (including ClearIcon)
+ App.Tap("Issue35736ToggleQueryIcon");
+ App.Tap("Issue35736ToggleClearIcon");
+ App.Tap("Issue35736ToggleClearPlaceholderIcon");
+
+ // Reset all back to defaults
+ App.Tap("Issue35736ResetAll");
+ App.WaitForElement("Issue35736QueryIconLabel");
+
+#if IOS
+ VerifyScreenshot(cropBottom:1000);
+#else
+ VerifyScreenshot();
+#endif
+ }
+}
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue35752.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue35752.cs
new file mode 100644
index 000000000000..254de90239ab
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue35752.cs
@@ -0,0 +1,36 @@
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues;
+
+public class Issue35752 : _IssuesUITest
+{
+ public override string Issue => "Android DragGestureRecognizer DragStarting fires prematurely on tap";
+
+ public Issue35752(TestDevice device)
+ : base(device)
+ { }
+
+ [Test]
+ [Category(UITestCategories.DragAndDrop)]
+ public void DragStartingShouldNotFireOnTapButShouldFireOnDrag()
+ {
+ App.WaitForElement("TestLoaded");
+
+ App.Tap("DragBox");
+
+ Thread.Sleep(600);
+
+ var dragCount = App.WaitForElement("DragStartCount").GetText();
+ Assert.That(dragCount, Is.EqualTo("0"),
+ "DragStarting should not fire on a quick tap");
+
+ // Initiate drag - DragStarting SHOULD fire
+ App.DragAndDrop("DragBox", "DropBox");
+
+ dragCount = App.WaitForElement("DragStartCount").GetText();
+ Assert.That(dragCount, Is.EqualTo("1"),
+ "DragStarting should fire when drag is initiated");
+ }
+}
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue35755.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue35755.cs
new file mode 100644
index 000000000000..9eeab645230b
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue35755.cs
@@ -0,0 +1,27 @@
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues;
+
+public class Issue35755 : _IssuesUITest
+{
+ public Issue35755(TestDevice device) : base(device)
+ {
+ }
+
+ public override string Issue => "IndexOutOfBoundsException in RecalculateSpanPositions when a Label uses FormattedText, MaxLines, and TailTruncation";
+
+ [Test]
+ [Category(UITestCategories.Label)]
+ public void FormattedTextWithMaxLinesAndTailTruncationShouldNotCrash()
+ {
+ App.WaitForElement("TriggerButton");
+ App.Tap("TriggerButton");
+
+ App.WaitForElement("ResultLabel");
+ var resultText = App.FindElement("ResultLabel").GetText();
+ Assert.That(resultText, Is.EqualTo("Success"), "Label should display truncated FormattedText without crashing.");
+ App.WaitForElement("CrashTargetLabel");
+ }
+}
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue35764.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue35764.cs
new file mode 100644
index 000000000000..0f215e66ae71
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue35764.cs
@@ -0,0 +1,26 @@
+#if TEST_FAILS_ON_WINDOWS //Issue Link - https://github.com/dotnet/maui/issues/28619
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues;
+
+public class Issue35764 : _IssuesUITest
+{
+ public Issue35764(TestDevice device)
+ : base(device)
+ {
+ }
+
+ public override string Issue => "[Android] SearchHandler.ClearPlaceholderEnabled has no effect";
+
+ [Test]
+ [Category(UITestCategories.Shell)]
+ public void ClearPlaceholderIconShouldHideWhenDisabled()
+ {
+ App.WaitForElement("ToggleClearPlaceholderEnabled");
+ App.Tap("ToggleClearPlaceholderEnabled");
+ VerifyScreenshot();
+ }
+}
+#endif
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue35771.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue35771.cs
new file mode 100644
index 000000000000..37a6c0a2e851
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue35771.cs
@@ -0,0 +1,48 @@
+// Crash is Android-specific: RenderThread GL functor receives zero-area Skia canvas when ClipBounds=(0,0,0,0) at (w>0,h=0)
+#if ANDROID
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues;
+
+public class Issue35771 : _IssuesUITest
+{
+ public Issue35771(TestDevice device) : base(device) { }
+
+ public override string Issue => "Android SIGSEGV crash with multiple auto-sizing WebViews in ScrollView on navigated page";
+
+ [Test]
+ [Category(UITestCategories.WebView)]
+ public void MultipleAutoSizingWebViewsInScrollViewShouldNotCrash()
+ {
+ App.WaitForElement("Issue35771NavigateButton");
+ App.Tap("Issue35771NavigateButton");
+ App.WaitForElement("Issue35771Ready");
+ App.Back();
+ App.WaitForElement("Issue35771NavigateButton");
+ }
+
+ [Test]
+ [Category(UITestCategories.WebView)]
+ public void HorizontalAutoSizingWebViewsShouldNotCrash()
+ {
+ App.WaitForElement("Issue35771HorizontalNavigateButton");
+ App.Tap("Issue35771HorizontalNavigateButton");
+ App.WaitForElement("Issue35771HorizontalReady");
+ App.Back();
+ App.WaitForElement("Issue35771NavigateButton");
+ }
+
+ [Test]
+ [Category(UITestCategories.WebView)]
+ public void PopAsyncFromAutoSizingWebViewPageShouldNotCrash()
+ {
+ App.WaitForElement("Issue35771PopAsyncNavigateButton");
+ App.Tap("Issue35771PopAsyncNavigateButton");
+ App.WaitForElement("Issue35771PopAsyncReady");
+ App.Tap("Issue35771PopAsyncPopButton");
+ App.WaitForElement("Issue35771NavigateButton");
+ }
+}
+#endif
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue35788.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue35788.cs
new file mode 100644
index 000000000000..42c5dadc5025
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue35788.cs
@@ -0,0 +1,32 @@
+#if ANDROID
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues;
+
+public class Issue35788 : _IssuesUITest
+{
+ public Issue35788(TestDevice device) : base(device)
+ {
+ }
+
+ public override string Issue => "[Android] WebView CanGoBack returns true unexpectedly on first page due to spurious about:blank history entry";
+
+ [Test]
+ [Category(UITestCategories.WebView)]
+ public void WebViewCanGoBackShouldBeFalseOnFirstPage()
+ {
+ App.WaitForElement("Issue35788NavigateButton");
+ App.Tap("Issue35788NavigateButton");
+
+ App.WaitForTextToBePresentInElement("Issue35788StatusLabel", "CanGoBack=");
+
+ var statusText = App.FindElement("Issue35788StatusLabel").GetText();
+
+ Assert.That(statusText, Is.EqualTo("CanGoBack=False"),
+ "WebView.CanGoBack should be false on the first navigated page. " +
+ "If true, the about:blank layout entry was not cleared from the native history stack.");
+ }
+}
+#endif
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue35806.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue35806.cs
new file mode 100644
index 000000000000..469c29468fbc
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue35806.cs
@@ -0,0 +1,43 @@
+#if ANDROID // This regression is Android-only: https://github.com/dotnet/maui/pull/29255
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues;
+
+public class Issue35806 : _IssuesUITest
+{
+ public Issue35806(TestDevice device) : base(device) { }
+
+ public override string Issue => "Android CollectionView KeepScrollOffset stops working after replacing ItemsSource";
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void KeepScrollOffsetWorksAfterReplacingItemsSource()
+ {
+ App.WaitForElement("CollectionView35806");
+
+ // Verify initial source loaded
+ App.WaitForElement("v1-Item 1");
+
+ // Replace source, scroll to top, insert at top — KeepScrollOffset should keep position
+ App.Click("ReplaceSourceButton");
+ App.WaitForElement("v2-Item 1");
+ App.Click("ScrollToTopButton");
+ App.Click("InsertAtTopButton");
+
+ // With KeepScrollOffset at position 0, the inserted item should be visible at the top.
+ // Without the fix, "Inserted-31" is hidden above the viewport (broken KeepItemsInView behavior).
+ App.WaitForElement("Inserted-31");
+
+ // Replace source again to verify it still works on subsequent replacements
+ App.Click("ReplaceSourceButton");
+ App.WaitForElement("v3-Item 1");
+ App.Click("ScrollToTopButton");
+ App.Click("InsertAtTopButton");
+
+ // After second replacement (30 items in new source), inserted item text is "Inserted-31"
+ App.WaitForElement("Inserted-31");
+ }
+}
+#endif
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue35844.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue35844.cs
new file mode 100644
index 000000000000..6ee9b0d9f4ef
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue35844.cs
@@ -0,0 +1,48 @@
+#if TEST_FAILS_ON_WINDOWS && TEST_FAILS_ON_CATALYST // SetOrientationLandscape/Portrait is only supported on iOS and Android.
+
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues
+{
+ public class Issue35844 : _IssuesUITest
+ {
+ public override string Issue => "Shell TitleView does not resize after rotation on iOS 26+";
+
+ public Issue35844(TestDevice device) : base(device) { }
+
+ [Test]
+ [Category(UITestCategories.Shell)]
+ public void ShellTitleViewResizesOnRotation()
+ {
+ App.WaitForElement("TitleViewGrid");
+ App.WaitForElement("StatusLabel");
+
+ // Capture portrait width
+ var portraitRect = App.WaitForElement("TitleViewGrid").GetRect();
+ var portraitWidth = portraitRect.Width;
+
+ App.SetOrientationLandscape();
+ App.WaitForElement("TitleViewGrid"); // re-wait to ensure layout has settled after rotation
+
+ // After rotation, TitleView width must change to fill the wider nav bar
+ var landscapeRect = App.WaitForElement("TitleViewGrid").GetRect();
+ var landscapeWidth = landscapeRect.Width;
+
+ Assert.That(landscapeWidth, Is.Not.EqualTo(portraitWidth).Within(50),
+ "Shell TitleView width should expand after rotating to landscape on iOS 26+");
+ Assert.That(landscapeWidth, Is.GreaterThan(portraitWidth),
+ "Shell TitleView should be wider in landscape than portrait");
+
+ // Rotate back and verify TitleView returns to original width
+ App.SetOrientationPortrait();
+ App.WaitForElement("TitleViewGrid"); // re-wait to ensure layout has settled after rotation
+
+ var finalRect = App.WaitForElement("TitleViewGrid").GetRect();
+ Assert.That(finalRect.Width, Is.EqualTo(portraitWidth).Within(5),
+ "Shell TitleView should return to original portrait width after rotating back");
+ }
+ }
+}
+#endif
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue35859.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue35859.cs
new file mode 100644
index 000000000000..33840a92c010
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue35859.cs
@@ -0,0 +1,32 @@
+#if TEST_FAILS_ON_WINDOWS && TEST_FAILS_ON_ANDROID // This test is specific to iOS/macOS CollectionView handler behavior.
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues;
+
+public class Issue35859 : _IssuesUITest
+{
+ public Issue35859(TestDevice device)
+ : base(device)
+ {
+ }
+
+ public override string Issue => "CollectionView2 on iOS measures non-first cells despite ItemSizingStrategy.MeasureFirstItem";
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void CollectionView2ShouldNotMeasureNonFirstItemsWithCachedFirstItemHeight()
+ {
+ App.WaitForElement("35859ResetButton");
+ App.Tap("35859ResetButton");
+
+ App.WaitForElement("35859ScrollTo40Button");
+ App.Tap("35859ScrollTo40Button");
+
+ var summary = App.WaitForElement("35859Summary").GetText();
+ Assert.That(summary, Does.Contain("Items2 CV2: 0 cached-height non-first"));
+
+ }
+}
+#endif
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue35902.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue35902.cs
new file mode 100644
index 000000000000..4f44b2446b24
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue35902.cs
@@ -0,0 +1,27 @@
+#if IOS // This test is only for iOS because the issue is specifically related to on-screen keyboard behavior which is only available on mobile platforms.
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues;
+
+public class Issue35902 : _IssuesUITest
+{
+ public Issue35902(TestDevice device) : base(device) { }
+
+ public override string Issue => "[iOS] Transparent Shell Navigation Bar Breaks After Keyboard Interaction on Secondary Pages";
+
+ [Test]
+ [Category(UITestCategories.Shell)]
+ public void TransparentShellNavBarShouldRemainTransparentAfterKeyboardDismiss()
+ {
+ App.WaitForElement("NavigateButton");
+ App.Tap("NavigateButton");
+ App.WaitForElement("TestEntry");
+ App.Tap("TestEntry");
+ App.DismissKeyboard();
+ App.WaitForElement("TestEntry");
+ VerifyScreenshot();
+ }
+}
+#endif
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue35943.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue35943.cs
new file mode 100644
index 000000000000..2354e03275df
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue35943.cs
@@ -0,0 +1,34 @@
+#if TEST_FAILS_ON_WINDOWS // BoxView AutomationId is not working on Windows. Related issue:https://github.com/dotnet/maui/issues/27195
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues
+{
+ public class Issue35943 : _IssuesUITest
+ {
+ public Issue35943(TestDevice device) : base(device) { }
+
+ public override string Issue => "[iOS, MacCatalyst] GetPosition Truncates Fractional Coordinates to Integers on TappedEvent";
+
+ [Test]
+ [Category(UITestCategories.Gestures)]
+ public void GetPositionPreservesFractionalCoordinates()
+ {
+ // The tap target is a BoxView. The reference box (ReferenceBox) has a 0.5-point
+ // margin, placing it at a fractional UIKit coordinate. GetPosition(relativeTo: ReferenceBox)
+ // should therefore return coordinates with a fractional component.
+ // Before the fix, an explicit (int) cast in CalculatePosition truncated these values.
+ var tapRect = App.WaitForElement("TapTarget").GetRect();
+
+ // Tap at integer screen coordinates so position relative to the 0.5-point reference box
+ // is expected to include a fractional component.
+ App.TapCoordinates((int)tapRect.CenterX(), (int)tapRect.CenterY());
+
+ // "Success" appears when the coordinates have a fractional component;
+ // "Failure" appears when they are truncated to integers.
+ App.WaitForElement("Success");
+ }
+ }
+}
+#endif
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue36154.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue36154.cs
index e005d404709b..44626a2afd21 100644
--- a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue36154.cs
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue36154.cs
@@ -28,7 +28,7 @@ public void Issue36154SwipeViewShouldRevealItems()
var centerY = rect.Y + rect.Height / 2;
// Swipe left (finger moves left) → reveals RightItems
- App.DragCoordinates(centerX, centerY, centerX - 200, centerY);
+ App.DragCoordinates(centerX, centerY, centerX - 300, centerY);
Assert.That(App.WaitForElement("ResultLabel").GetText(), Is.EqualTo("RIGHT invoked!"));
}
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue36853.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue36853.cs
new file mode 100644
index 000000000000..9c3efd79944d
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue36853.cs
@@ -0,0 +1,42 @@
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues;
+
+public class Issue36853 : _IssuesUITest
+{
+ public override string Issue => "Shell singleton page renders blank when re-pushed after absolute route PopToRoot on Android";
+
+ public Issue36853(TestDevice testDevice) : base(testDevice)
+ {
+ }
+
+ [Test]
+ [Category(UITestCategories.Shell)]
+ public void SingletonPageShouldRenderAfterPopToRootAndRePush()
+ {
+ // Step 1: From root, push SecondPage (singleton)
+ App.WaitForElement("Issue36853GoToSecond");
+ App.Tap("Issue36853GoToSecond");
+
+ // Step 2: Verify SecondPage renders
+ App.WaitForElement("Issue36853SecondLabel");
+
+ // Step 3: Push ThirdPage on top (so stack is Root → Second → Third)
+ App.Tap("Issue36853GoToThird");
+ App.WaitForElement("Issue36853ThirdLabel");
+
+ // Step 4: PopToRoot via absolute route ///
+ App.Tap("Issue36853ResetToRoot");
+ App.WaitForElement("Issue36853MainLabel");
+
+ // Step 5: Re-push SecondPage (same singleton instance)
+ App.Tap("Issue36853GoToSecond");
+
+ // Step 6: SecondPage content must be visible — not blank
+ // Without the fix, WaitForElement will timeout because the stale fragment
+ // has a disconnected handler and OnCreateView never fires — page is blank.
+ App.WaitForElement("Issue36853SecondLabel");
+ }
+}
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue36942.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue36942.cs
new file mode 100644
index 000000000000..2725a0f2899d
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue36942.cs
@@ -0,0 +1,26 @@
+#if TEST_FAILS_ON_WINDOWS //Issue Link : https://github.com/dotnet/maui/issues/4715
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues
+{
+ public class Issue36942 : _IssuesUITest
+ {
+ public Issue36942(TestDevice device) : base(device)
+ {
+ }
+ public override string Issue => "Border with Shadow breaks descendant BackgroundColor UI updates on Android";
+
+ [Test]
+ [Category(UITestCategories.Shadow)]
+ public void TappingDescendantInsideShadowedBorderShouldUpdateBackgroundColor()
+ {
+ App.WaitForElement("Issue36942Page");
+ App.Tap("ToggleTarget");
+ App.WaitForTextToBePresentInElement("ViewModelState", "Activated: True");
+ VerifyScreenshot();
+ }
+ }
+}
+#endif
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue4715.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue4715.cs
index 3297ace45fcd..af668da6bc8f 100644
--- a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue4715.cs
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue4715.cs
@@ -11,72 +11,85 @@ public Issue4715(TestDevice device) : base(device) { }
public override string Issue => "[Windows] Layout containers not visible to UI automation";
[Test]
- [Category(UITestCategories.Layout)]
- public void GridWithAutomationIdIsFoundByAppium()
+ [Category(UITestCategories.Accessibility)]
+ public void GridWithAutomationIdOnlyIsFoundByAppium()
{
App.WaitForElement("WaitForStubControl");
- // Grid with AutomationId must be visible in the UIA tree
+ // AutomationId-only layouts must remain visible to Windows UI tests.
App.WaitForElement("TestGrid");
}
[Test]
- [Category(UITestCategories.Layout)]
- public void VerticalStackLayoutWithAutomationIdIsFoundByAppium()
+ [Category(UITestCategories.Accessibility)]
+ public void VerticalStackLayoutWithAccessibleTreeOptInIsFoundByAppium()
{
App.WaitForElement("WaitForStubControl");
- // VerticalStackLayout with AutomationId must be visible in the UIA tree
+ // VerticalStackLayout with explicit accessible-tree opt-in must be visible in the UIA tree.
App.WaitForElement("TestVerticalStackLayout");
}
[Test]
- [Category(UITestCategories.Layout)]
- public void HorizontalStackLayoutWithAutomationIdIsFoundByAppium()
+ [Category(UITestCategories.Accessibility)]
+ public void HorizontalStackLayoutWithAccessibleTreeOptInIsFoundByAppium()
{
App.WaitForElement("WaitForStubControl");
- // HorizontalStackLayout with AutomationId must be visible in the UIA tree
+ // HorizontalStackLayout with explicit accessible-tree opt-in must be visible in the UIA tree.
App.WaitForElement("TestHorizontalStackLayout");
}
[Test]
- [Category(UITestCategories.Layout)]
- public void FlexLayoutWithAutomationIdIsFoundByAppium()
+ [Category(UITestCategories.Accessibility)]
+ public void FlexLayoutWithAccessibleTreeOptInIsFoundByAppium()
{
App.WaitForElement("WaitForStubControl");
- // FlexLayout with AutomationId must be visible in the UIA tree
+ // FlexLayout with explicit accessible-tree opt-in must be visible in the UIA tree.
App.WaitForElement("TestFlexLayout");
}
[Test]
- [Category(UITestCategories.Layout)]
- public void AbsoluteLayoutWithAutomationIdIsFoundByAppium()
+ [Category(UITestCategories.Accessibility)]
+ public void AbsoluteLayoutWithAccessibleTreeOptInIsFoundByAppium()
{
App.WaitForElement("WaitForStubControl");
- // AbsoluteLayout with AutomationId must be visible in the UIA tree
+ // AbsoluteLayout with explicit accessible-tree opt-in must be visible in the UIA tree.
App.WaitForElement("TestAbsoluteLayout");
}
[Test]
- [Category(UITestCategories.Layout)]
- public void NestedOuterLayoutWithAutomationIdIsFoundByAppium()
+ [Category(UITestCategories.Accessibility)]
+ public void NestedOuterLayoutWithAccessibleTreeOptInIsFoundByAppium()
{
App.WaitForElement("WaitForStubControl");
- // Outer nested Grid with AutomationId must be visible
+ // Outer nested Grid with explicit accessible-tree opt-in must be visible.
App.WaitForElement("TestNestedOuterGrid");
}
[Test]
- [Category(UITestCategories.Layout)]
- public void AnonymousLayoutWithoutAutomationIdIsNotFoundByAppium()
+ [Category(UITestCategories.Accessibility)]
+ public void LayoutWithAccessibleTreeOptOutIsNotFoundByAppium()
{
+ // Removing an AutomationId-bearing element from the accessibility tree via
+ // IsInAccessibleTree="False" hides it from the Windows UIA Control view, so Appium can no
+ // longer find it. This is Windows-specific behavior: on other platforms an element keeps its
+ // AutomationId/AccessibilityIdentifier and stays discoverable by Appium regardless of the
+ // accessible-tree opt-out, so this assertion only holds on Windows.
+ if (Device != TestDevice.Windows)
+ {
+ Assert.Ignore("Accessible-tree opt-out from the UIA Control view is Windows-specific behavior.");
+ }
+
App.WaitForElement("WaitForStubControl");
- // Anonymous Grid (no AutomationId) must NOT be found in the UIA tree
- App.WaitForNoElement("Anonymous Grid");
+ // A layout with an AutomationId but an explicit IsInAccessibleTree="False" opts out of the
+ // UIA Control view. Appium must NOT find it by its AutomationId, proving the Raw opt-out takes
+ // precedence over the AutomationId discoverability hook. Anonymous (no-AutomationId) layout
+ // exclusion is covered authoritatively by the LayoutPanel device tests.
+ App.WaitForNoElement("OptedOutGrid");
}
}
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue6016.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue6016.cs
new file mode 100644
index 000000000000..7d56603de25f
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue6016.cs
@@ -0,0 +1,122 @@
+#if TEST_FAILS_ON_WINDOWS // AutomationId for SwipeItem is not being set on Windows, causing test failures.
+using System.Drawing;
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues
+{
+ public class Issue6016 : _IssuesUITest
+ {
+ public override string Issue => "SwipeView Threshold changes width and offset of the side menu";
+
+ public Issue6016(TestDevice device) : base(device) { }
+
+ // Waits up to `timeout` for the element's X position to satisfy `predicate`.
+ // Returns the last observed X value.
+ static float WaitForXPosition(IApp app, string automationId, Func predicate,
+ TimeSpan? timeout = null)
+ {
+ timeout ??= TimeSpan.FromSeconds(3);
+ var retryFrequency = TimeSpan.FromMilliseconds(200);
+ var deadline = DateTime.Now + timeout.Value;
+ float x = 0;
+ do
+ {
+ x = app.WaitForElement(automationId).GetRect().X;
+ if (predicate(x))
+ break;
+ Task.Delay(retryFrequency).Wait();
+ }
+ while (DateTime.Now < deadline);
+ return x;
+ }
+
+ // Parameterized over swipe direction:
+ // swipeRight=true → LeftItems (drag right, content moves right when open)
+ // swipeRight=false → RightItems (drag left, content moves left when open)
+ //
+ // Drag distance is 50% of SwipeView width — density-independent; always exceeds
+ // the ~100dp snap threshold regardless of screen density.
+ [TestCase("DefaultContent", "ThresholdContent", "DefaultSwipeView", "ThresholdSwipeView", true, TestName = "LeftItems")]
+ [TestCase("DefaultRightContent", "ThresholdRightContent", "DefaultRightSwipeView", "ThresholdRightSwipeView", false, TestName = "RightItems")]
+ [Category(UITestCategories.SwipeView)]
+ public void SwipeViewThresholdShouldNotChangeMenuWidth(
+ string defaultContentId, string thresholdContentId,
+ string defaultSwipeViewId, string thresholdSwipeViewId,
+ bool swipeRight)
+ {
+ App.ScrollTo(defaultContentId);
+
+ // Open the default (no-threshold) SwipeView and measure content displacement
+ var defaultSwipeRect = App.WaitForElement(defaultSwipeViewId).GetRect();
+ float initialDefaultX = App.WaitForElement(defaultContentId).GetRect().X;
+ OpenSwipeView(defaultSwipeRect, swipeRight);
+ float openDefaultX = WaitForXPosition(App, defaultContentId,
+ x => swipeRight ? x > initialDefaultX + 5 : x < initialDefaultX - 5);
+ float defaultMenuWidth = Math.Abs(openDefaultX - initialDefaultX);
+
+ // Close the default SwipeView before opening the threshold one.
+ // A scroll may be needed between them; leaving it open while scrolling
+ // causes the subsequent drag to not register on the threshold SwipeView.
+ App.TapCoordinates(
+ swipeRight ? openDefaultX + 50 : openDefaultX - 50,
+ defaultSwipeRect.CenterY());
+ WaitForXPosition(App, defaultContentId,
+ x => swipeRight ? x <= initialDefaultX + 5 : x >= initialDefaultX - 5);
+
+ // Open the threshold (Threshold=200) SwipeView and measure content displacement
+ App.ScrollTo(thresholdContentId);
+ var thresholdSwipeRect = App.WaitForElement(thresholdSwipeViewId).GetRect();
+ float initialThresholdX = App.WaitForElement(thresholdContentId).GetRect().X;
+ OpenSwipeView(thresholdSwipeRect, swipeRight);
+ float openThresholdX = WaitForXPosition(App, thresholdContentId,
+ x => swipeRight ? x > initialThresholdX + 5 : x < initialThresholdX - 5);
+ float thresholdMenuWidth = Math.Abs(openThresholdX - initialThresholdX);
+
+ Assert.That(thresholdMenuWidth, Is.EqualTo(defaultMenuWidth).Within(5),
+ $"SwipeView menu width should not change with Threshold. " +
+ $"Default={defaultMenuWidth:F1}px, Threshold=200 → {thresholdMenuWidth:F1}px");
+ }
+
+ // Opens a SwipeView by dragging 50% of its width — density-independent gesture.
+ void OpenSwipeView(Rectangle swipeViewRect, bool swipeRight)
+ {
+ float centerY = swipeViewRect.CenterY();
+ float halfWidth = swipeViewRect.Width * 0.5f;
+ if (swipeRight)
+ App.DragCoordinates(swipeViewRect.X + 10, centerY, swipeViewRect.X + halfWidth, centerY);
+ else
+ App.DragCoordinates(swipeViewRect.X + swipeViewRect.Width - 10, centerY, swipeViewRect.X + halfWidth, centerY);
+ }
+
+ [Test]
+ [Category(UITestCategories.SwipeView)]
+ public void SwipeViewExecuteModeTriggers()
+ {
+ // Verify that SwipeMode.Execute triggers the SwipeItem when the swipe exceeds
+ // the open distance (~80% of content width). Guards against regressions where
+ // GetSwipeItemSize returns wrong size for Execute mode (e.g. 100dp instead of
+ // contentWidth / items.Count), which would cause a visible gap during the swipe.
+
+ App.ScrollTo("ExecuteContent");
+
+ // Use SwipeView bounds for drag — the inner label is much narrower and
+ // a label-relative drag would fall far short of the 48% trigger threshold.
+ var executeRect = App.WaitForElement("ExecuteSwipeView").GetRect();
+
+ // Drag right by 90% of SwipeView width — density-independent, exceeds ~80% trigger
+ App.DragCoordinates(
+ executeRect.X + 10, executeRect.CenterY(),
+ executeRect.X + executeRect.Width * 0.9f, executeRect.CenterY());
+
+ // After Execute mode triggers, SwipeView snaps closed; wait for result label
+ bool executed = App.WaitForTextToBePresentInElement("ExecuteResultLabel", "Executed",
+ timeout: TimeSpan.FromSeconds(3));
+
+ Assert.That(executed, Is.True,
+ "SwipeItem.Invoked should have fired after swiping past the Execute mode threshold");
+ }
+ }
+}
+#endif
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue7580.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue7580.cs
index 6c65985cec6e..400a25a3fede 100644
--- a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue7580.cs
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue7580.cs
@@ -22,10 +22,11 @@ public void SwipeItemVisibilityChangeShouldNotInvokeTwice()
var initialCount = App.FindElement("InvokeCountLabel").GetText();
Assert.That(initialCount, Is.EqualTo("InvokeCount: 0"));
- var rect = App.WaitForElement("SwipeTarget").GetRect();
- var centerY = rect.Y + rect.Height / 2;
- var startX = rect.X + 20;
- var endX = startX + 600;
+ var contentRect = App.WaitForElement("SwipeContent").GetRect();
+ var centerY = contentRect.Y + contentRect.Height / 2;
+ // Here contentRect.X is negative value on mac, so we need to make sure we don't start dragging from a negative X coordinate
+ var startX = Math.Max(contentRect.X + 20, 0);
+ var endX = contentRect.X + contentRect.Width - 5;
App.DragCoordinates(startX, centerY, endX, centerY);
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue7814.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue7814.cs
index 1a129870b771..1d3bc270991c 100644
--- a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue7814.cs
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue7814.cs
@@ -13,6 +13,10 @@ public class Issue7814 : _IssuesUITest
const string OuterScrollViewId = "Issue7814OuterScrollView";
const string VerticalOffsetLabelId = "Issue7814VerticalScrollYLabel";
const string HorizontalOffsetLabelId = "Issue7814HorizontalScrollXLabel";
+ const string TouchParentPositionLabelId = "Issue7814TouchParentPositionLabel";
+ const string TouchStatusLabelId = "Issue7814TouchStatusLabel";
+ const string TouchClaimViewId = "Issue7814TouchClaimView";
+ const string TouchReleaseViewId = "Issue7814TouchReleaseView";
public Issue7814(TestDevice testDevice) : base(testDevice)
{
@@ -59,6 +63,78 @@ public void VerticalScrollFromCarouselWorksAfterHorizontalScrollViewGesture()
});
}
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void TouchClaimingRowInsideVerticalCollectionViewNestedInHorizontalParentKeepsHorizontalGesture()
+ {
+ if (App is not AppiumAndroidApp)
+ {
+ Assert.Ignore("The Issue7814 touch-dispatch change is Android-specific.");
+ }
+
+ App.WaitForElement(OuterScrollViewId);
+ ScrollUntilVisible(TouchClaimViewId);
+
+ var parentPositionBeforeGesture = GetTouchParentPosition();
+ var touchViewRect = GetVisibleRect(TouchClaimViewId);
+
+ App.DragCoordinates(
+ touchViewRect.Right - 20,
+ touchViewRect.Top + (touchViewRect.Height / 2),
+ touchViewRect.Left + 20,
+ touchViewRect.Top + (touchViewRect.Height / 2));
+
+ App.RetryAssert(() =>
+ {
+ var touchStatusAfterGesture = App.FindElement(TouchStatusLabelId).GetText();
+ var parentPositionAfterGesture = GetTouchParentPosition();
+
+ Assert.Multiple(() =>
+ {
+ Assert.That(touchStatusAfterGesture, Does.Contain("Up"), "The row touch-claiming view should receive the whole drag.");
+ Assert.That(parentPositionAfterGesture, Is.EqualTo(parentPositionBeforeGesture), "The horizontal parent CarouselView should not steal the claimed row gesture.");
+ });
+ });
+ }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ public void TouchReleasingRowInsideVerticalCollectionViewNestedInHorizontalParentHandsHorizontalGestureToParent()
+ {
+ if (App is not AppiumAndroidApp)
+ {
+ Assert.Ignore("The Issue7814 touch-dispatch change is Android-specific.");
+ }
+
+ App.WaitForElement(OuterScrollViewId);
+ ScrollUntilVisible(TouchReleaseViewId);
+
+ var parentPositionBeforeGesture = GetTouchParentPosition();
+ var touchViewRect = GetVisibleRect(TouchReleaseViewId);
+
+ App.DragCoordinates(
+ touchViewRect.Right - 20,
+ touchViewRect.Top + (touchViewRect.Height / 2),
+ touchViewRect.Left + 20,
+ touchViewRect.Top + (touchViewRect.Height / 2));
+
+ App.RetryAssert(() =>
+ {
+ Assert.That(GetTouchParentPosition(), Is.GreaterThan(parentPositionBeforeGesture), "The horizontal parent CarouselView should take over after the row releases the gesture.");
+ });
+
+ App.RetryAssert(() =>
+ {
+ var touchStatusAfterGesture = App.FindElement(TouchStatusLabelId).GetText();
+
+ Assert.Multiple(() =>
+ {
+ Assert.That(touchStatusAfterGesture, Does.Contain("Cancel"), "The row touch-claiming view should be cancelled after releasing the gesture.");
+ Assert.That(touchStatusAfterGesture, Does.Not.Contain("Up"), "The row touch-claiming view should not complete a released gesture.");
+ });
+ });
+ }
+
void DragWithinVisibleArea(string automationId, double fromXRatio, double fromYRatio, double toXRatio, double toYRatio)
{
var visibleRect = GetVisibleRect(automationId);
@@ -75,6 +151,21 @@ void DragWithinRect(Rectangle visibleRect, double fromXRatio, double fromYRatio,
App.DragCoordinates(fromX, fromY, toX, toY);
}
+ void ScrollUntilVisible(string automationId)
+ {
+ for (var attempt = 0; attempt < 6; attempt++)
+ {
+ if (IsVisibleEnough(automationId))
+ {
+ return;
+ }
+
+ App.ScrollDown(OuterScrollViewId, ScrollStrategy.Gesture, swipePercentage: 0.75);
+ }
+
+ Assert.Fail($"{automationId} should become visible after scrolling {OuterScrollViewId}.");
+ }
+
Rectangle GetVisibleRect(string automationId)
{
var elementRect = App.WaitForElement(automationId).GetRect();
@@ -87,6 +178,22 @@ Rectangle GetVisibleRect(string automationId)
return visibleRect;
}
+ bool IsVisibleEnough(string automationId)
+ {
+ try
+ {
+ var elementRect = App.WaitForElement(automationId).GetRect();
+ var viewportRect = App.WaitForElement(OuterScrollViewId).GetRect();
+ var visibleRect = Rectangle.Intersect(elementRect, viewportRect);
+
+ return visibleRect.Width > 40 && visibleRect.Height > 40;
+ }
+ catch
+ {
+ return false;
+ }
+ }
+
static float GetCoordinate(int start, int length, double ratio)
{
return (float)(start + (length * ratio));
@@ -96,6 +203,8 @@ static float GetCoordinate(int start, int length, double ratio)
int GetHorizontalOffset() => GetOffset(HorizontalOffsetLabelId);
+ int GetTouchParentPosition() => GetOffset(TouchParentPositionLabelId);
+
int GetOffset(string automationId)
{
var text = App.FindElement(automationId).GetText();
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue8680.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue8680.cs
new file mode 100644
index 000000000000..d53179569ca7
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue8680.cs
@@ -0,0 +1,39 @@
+#if ANDROID // Android-specific: OnBackButtonPressed uses onBackPressedDispatcher
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues;
+
+public class Issue8680 : _IssuesUITest
+{
+ public Issue8680(TestDevice device) : base(device) { }
+
+ public override string Issue => "Rework OnBackButtonPressed to use onBackPressedDispatcher";
+
+ [Test]
+ [Category(UITestCategories.Navigation)]
+ public void BackButtonPressIsInterceptedByOnBackButtonPressed()
+ {
+ // Navigate to the intercept page
+ App.WaitForElement("NavigateButton");
+ App.Tap("NavigateButton");
+
+ // Confirm we are on the intercept page
+ App.WaitForElement("InterceptPageLabel");
+
+ // Press the device back button — should be intercepted (page stays)
+ App.Back();
+
+ // The page should still be visible because OnBackButtonPressed returned true
+ App.WaitForElement("StatusLabel");
+ Assert.That(
+ App.FindElement("StatusLabel").GetText(),
+ Is.EqualTo("Back intercepted: 1"),
+ "OnBackButtonPressed should have been called exactly once per back press (detects dual-fire regression on API 33+).");
+
+ // The intercept page should still be displayed (not popped)
+ App.WaitForElement("InterceptPageLabel");
+ }
+}
+#endif
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/IndicatorViewCircleShape.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/IndicatorViewCircleShape.png
new file mode 100644
index 000000000000..71dd341017cb
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/IndicatorViewCircleShape.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/IndicatorViewSquareShape.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/IndicatorViewSquareShape.png
new file mode 100644
index 000000000000..bebabab51bef
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/IndicatorViewSquareShape.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Issue35216SwipeOpen_BecomeVisible.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Issue35216SwipeOpen_BecomeVisible.png
new file mode 100644
index 000000000000..abb6e13951f2
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Issue35216SwipeOpen_BecomeVisible.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Issue35216SwipeOpen_DeleteHidden.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Issue35216SwipeOpen_DeleteHidden.png
new file mode 100644
index 000000000000..dfe6142c1a3a
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Issue35216SwipeOpen_DeleteHidden.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Issue35216SwipeOpen_DeleteVisible.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Issue35216SwipeOpen_DeleteVisible.png
new file mode 100644
index 000000000000..80ce7ad55da8
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Issue35216SwipeOpen_DeleteVisible.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Issue35216SwipeOpen_InitiallyHidden.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Issue35216SwipeOpen_InitiallyHidden.png
new file mode 100644
index 000000000000..58f22c47bfca
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/Issue35216SwipeOpen_InitiallyHidden.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/MenuBarItem_FileMenuExit.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/MenuBarItem_FileMenuExit.png
new file mode 100644
index 000000000000..5083663bacfd
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/MenuBarItem_FileMenuExit.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/MenuBarItem_MediaMenuBarItemPresent.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/MenuBarItem_MediaMenuBarItemPresent.png
new file mode 100644
index 000000000000..f60faeb56a1c
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/MenuBarItem_MediaMenuBarItemPresent.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/MenuBarItem_MenuFlyoutSeparatorPresent.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/MenuBarItem_MenuFlyoutSeparatorPresent.png
new file mode 100644
index 000000000000..f84522181acc
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/MenuBarItem_MenuFlyoutSeparatorPresent.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/MenuBarItem_RefreshMenuItemProperties.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/MenuBarItem_RefreshMenuItemProperties.png
new file mode 100644
index 000000000000..b50e3968ece0
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/MenuBarItem_RefreshMenuItemProperties.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/RadioButton_Checking_Initial_Configuration_VerifyVisualState.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/RadioButton_Checking_Initial_Configuration_VerifyVisualState.png
deleted file mode 100644
index 880f948b0c46..000000000000
Binary files a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/RadioButton_Checking_Initial_Configuration_VerifyVisualState.png and /dev/null differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/RadioButton_SetContentAndTextTransform.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/RadioButton_SetContentAndTextTransform.png
new file mode 100644
index 000000000000..c4b63a0940f7
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/RadioButton_SetContentAndTextTransform.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/RadioButton_SetFontAutoScalingEnabled_VerifyVisualState.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/RadioButton_SetFontAutoScalingEnabled_VerifyVisualState.png
new file mode 100644
index 000000000000..eded8147d34e
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/RadioButton_SetFontAutoScalingEnabled_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/RadioButton_SetFontFamilyAndTextTransform_VerifyVisualState.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/RadioButton_SetFontFamilyAndTextTransform_VerifyVisualState.png
new file mode 100644
index 000000000000..6d28394c29f7
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/RadioButton_SetFontFamilyAndTextTransform_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/SearchBarClearButtonShouldBeVisibleWithText.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/SearchBarClearButtonShouldBeVisibleWithText.png
new file mode 100644
index 000000000000..a48ab8353549
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/SearchBarClearButtonShouldBeVisibleWithText.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/SearchBarClearButtonShouldDisappearAfterClearingInput.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/SearchBarClearButtonShouldDisappearAfterClearingInput.png
new file mode 100644
index 000000000000..1c355cdc7ef4
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/SearchBarClearButtonShouldDisappearAfterClearingInput.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/SearchHandlerQueryIconUpdatesAtRuntime.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/SearchHandlerQueryIconUpdatesAtRuntime.png
new file mode 100644
index 000000000000..f7e25391235d
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/SearchHandlerQueryIconUpdatesAtRuntime.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/SearchHandlerResetAllRestoresDefaultIcons.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/SearchHandlerResetAllRestoresDefaultIcons.png
new file mode 100644
index 000000000000..cf2ed073ac04
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/SearchHandlerResetAllRestoresDefaultIcons.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/TabTitlesShouldNotBeTruncated.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/TabTitlesShouldNotBeTruncated.png
deleted file mode 100644
index 33362207645b..000000000000
Binary files a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/TabTitlesShouldNotBeTruncated.png and /dev/null differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyAnchorXAndAnchorYShadow.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyAnchorXAndAnchorYShadow.png
new file mode 100644
index 000000000000..d076a533fcfb
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyAnchorXAndAnchorYShadow.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyAnchorXAndShadow.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyAnchorXAndShadow.png
new file mode 100644
index 000000000000..b5b8fdf5d34b
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyAnchorXAndShadow.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyAnchorYAndShadow.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyAnchorYAndShadow.png
new file mode 100644
index 000000000000..00e9a17120a2
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyAnchorYAndShadow.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyBorderWithNullStrokeDashArray.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyBorderWithNullStrokeDashArray.png
new file mode 100644
index 000000000000..4b29b526c667
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyBorderWithNullStrokeDashArray.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyBorderWithStrokeDashArrayValue.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyBorderWithStrokeDashArrayValue.png
new file mode 100644
index 000000000000..eb5ba976cfe3
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyBorderWithStrokeDashArrayValue.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyCarouselViewKeepScrollOffsetAdd.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyCarouselViewKeepScrollOffsetAdd.png
new file mode 100644
index 000000000000..1f750dcf13cb
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyCarouselViewKeepScrollOffsetAdd.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyDefaultScrollToRequested.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyDefaultScrollToRequested.png
deleted file mode 100644
index 654d87ae3a3a..000000000000
Binary files a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyDefaultScrollToRequested.png and /dev/null differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyEditorBackgroundColorResetToNone.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyEditorBackgroundColorResetToNone.png
new file mode 100644
index 000000000000..56142e93fd67
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyEditorBackgroundColorResetToNone.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyEditorBackgroundColorWithPlaceholder.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyEditorBackgroundColorWithPlaceholder.png
new file mode 100644
index 000000000000..194f5ff4b75c
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyEditorBackgroundColorWithPlaceholder.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyEditorBackgroundColorWithTextColor.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyEditorBackgroundColorWithTextColor.png
new file mode 100644
index 000000000000..aa705c7f344f
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyEditorBackgroundColorWithTextColor.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyEditorControlWhenFlowDirectionSet.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyEditorControlWhenFlowDirectionSet.png
index c1a990312aae..9c135e38d504 100644
Binary files a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyEditorControlWhenFlowDirectionSet.png and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyEditorControlWhenFlowDirectionSet.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyEditorHorizontalTextAlignmentWhenVerticalTextAlignmentSet.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyEditorHorizontalTextAlignmentWhenVerticalTextAlignmentSet.png
index f378504695e6..1158ee549abd 100644
Binary files a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyEditorHorizontalTextAlignmentWhenVerticalTextAlignmentSet.png and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyEditorHorizontalTextAlignmentWhenVerticalTextAlignmentSet.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyEditorPlaceholderTextWhenFontAttributesBoldAndItalicSet.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyEditorPlaceholderTextWhenFontAttributesBoldAndItalicSet.png
new file mode 100644
index 000000000000..ca10b81f171a
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyEditorPlaceholderTextWhenFontAttributesBoldAndItalicSet.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyEditorTextColorSetDefaultValue.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyEditorTextColorSetDefaultValue.png
new file mode 100644
index 000000000000..b272ac7612a4
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyEditorTextColorSetDefaultValue.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyEditorTextWhenAlignedHorizontally.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyEditorTextWhenAlignedHorizontally.png
new file mode 100644
index 000000000000..c96c7e38f315
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyEditorTextWhenAlignedHorizontally.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyEditorTextWhenAlingnedVertically.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyEditorTextWhenAlignedVertically.png
similarity index 100%
rename from src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyEditorTextWhenAlingnedVertically.png
rename to src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyEditorTextWhenAlignedVertically.png
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyEditorTextWhenAlingnedHorizontally.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyEditorTextWhenAlingnedHorizontally.png
deleted file mode 100644
index e2a53209c26c..000000000000
Binary files a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyEditorTextWhenAlingnedHorizontally.png and /dev/null differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyEditorTextWhenAutoSizeDisabled.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyEditorTextWhenAutoSizeDisabled.png
new file mode 100644
index 000000000000..1c291bbd4d80
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyEditorTextWhenAutoSizeDisabled.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyEditorTextWhenAutoSizeTextChangesSet.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyEditorTextWhenAutoSizeTextChangesSet.png
new file mode 100644
index 000000000000..a2eea6cf7f52
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyEditorTextWhenAutoSizeTextChangesSet.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyEditorTextWhenAutoSizeTextChangesSetWithHeightRequest.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyEditorTextWhenAutoSizeTextChangesSetWithHeightRequest.png
new file mode 100644
index 000000000000..74d31c64efb2
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyEditorTextWhenAutoSizeTextChangesSetWithHeightRequest.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyEditorTextWhenAutoSizeTextChangesSetWithShortShrinkText_LongText.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyEditorTextWhenAutoSizeTextChangesSetWithShortShrinkText_LongText.png
new file mode 100644
index 000000000000..9973c98348ce
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyEditorTextWhenAutoSizeTextChangesSetWithShortShrinkText_LongText.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyEditorTextWhenAutoSizeTextChangesSetWithShortShrinkText_ShortText.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyEditorTextWhenAutoSizeTextChangesSetWithShortShrinkText_ShortText.png
new file mode 100644
index 000000000000..5cb4ebb8d91a
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyEditorTextWhenAutoSizeTextChangesSetWithShortShrinkText_ShortText.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyEditorTextWhenFontAttributesBoldAndItalicSet.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyEditorTextWhenFontAttributesBoldAndItalicSet.png
new file mode 100644
index 000000000000..da21ea222753
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyEditorTextWhenFontAttributesBoldAndItalicSet.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyEditorTextWhenFontAttributesSet.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyEditorTextWhenFontAttributesSet.png
index c2b1cfeccef4..eb4a2443cc27 100644
Binary files a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyEditorTextWhenFontAttributesSet.png and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyEditorTextWhenFontAttributesSet.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyEditorVerticalTextAlignmentBasedOnCharacterSpacing.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyEditorVerticalTextAlignmentBasedOnCharacterSpacing.png
index 24cdde77b066..92a2128c14ca 100644
Binary files a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyEditorVerticalTextAlignmentBasedOnCharacterSpacing.png and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyEditorVerticalTextAlignmentBasedOnCharacterSpacing.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyEditorWhenBackgroundColorSet.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyEditorWhenBackgroundColorSet.png
new file mode 100644
index 000000000000..bb2fe2540337
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyEditorWhenBackgroundColorSet.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyEditorWhenHeightAndWidthRequestSet.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyEditorWhenHeightAndWidthRequestSet.png
new file mode 100644
index 000000000000..704a4c66e487
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyEditorWhenHeightAndWidthRequestSet.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyEditorWhenHeightRequestSet.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyEditorWhenHeightRequestSet.png
new file mode 100644
index 000000000000..7384e5ff3ef9
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyEditorWhenHeightRequestSet.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyEditorWhenOpacityResetToDefault.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyEditorWhenOpacityResetToDefault.png
new file mode 100644
index 000000000000..fb7153c1e3b0
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyEditorWhenOpacityResetToDefault.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyEditorWhenOpacitySet.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyEditorWhenOpacitySet.png
new file mode 100644
index 000000000000..6625ecff104b
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyEditorWhenOpacitySet.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyEditorWhenOpacitySetToZero.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyEditorWhenOpacitySetToZero.png
new file mode 100644
index 000000000000..b69cc0de24be
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyEditorWhenOpacitySetToZero.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyEditorWhenWidthRequestSet.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyEditorWhenWidthRequestSet.png
new file mode 100644
index 000000000000..58dcf388817c
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyEditorWhenWidthRequestSet.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyFlyoutPageCollapsedPaneWidth.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyFlyoutPageCollapsedPaneWidth.png
new file mode 100644
index 000000000000..e0272f3139f2
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyFlyoutPageCollapsedPaneWidth.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyHorizontalScrollViewPositionAtRuntime.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyHorizontalScrollViewPositionAtRuntime.png
new file mode 100644
index 000000000000..02556d48e5b0
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyHorizontalScrollViewPositionAtRuntime.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyRotationAndShadow.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyRotationAndShadow.png
new file mode 100644
index 000000000000..f84ede79e73e
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyRotationAndShadow.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyRotationXAndShadow.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyRotationXAndShadow.png
new file mode 100644
index 000000000000..31cbdc0b5e22
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyRotationXAndShadow.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyRotationYAndShadow.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyRotationYAndShadow.png
new file mode 100644
index 000000000000..e64a29020017
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyRotationYAndShadow.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyScaleAndShadow.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyScaleAndShadow.png
new file mode 100644
index 000000000000..452476b0fa6e
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyScaleAndShadow.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyScaleXAndShadow.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyScaleXAndShadow.png
new file mode 100644
index 000000000000..6595d283d04a
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyScaleXAndShadow.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyScaleYAndShadow.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyScaleYAndShadow.png
new file mode 100644
index 000000000000..f01f9b6bb762
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyScaleYAndShadow.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyScrollViewDirection.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyScrollViewDirection.png
new file mode 100644
index 000000000000..cd3430e7e124
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyScrollViewDirection.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyScrollViewHeightWhenRemoveChildAtRuntime.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyScrollViewHeightWhenRemoveChildAtRuntime.png
new file mode 100644
index 000000000000..727782a4f968
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyScrollViewHeightWhenRemoveChildAtRuntime.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifySwipeViewApperance.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifySwipeViewApperance.png
index f9d69fa28b7f..55ae781166a2 100644
Binary files a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifySwipeViewApperance.png and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifySwipeViewApperance.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyTranslationXAndShadow.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyTranslationXAndShadow.png
new file mode 100644
index 000000000000..70adfd1b48b1
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyTranslationXAndShadow.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyTranslationYAndShadow.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyTranslationYAndShadow.png
new file mode 100644
index 000000000000..bd5f64888d65
Binary files /dev/null and b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyTranslationYAndShadow.png differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyzEditorTextWhenAutoSizeDisabled.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyzEditorTextWhenAutoSizeDisabled.png
deleted file mode 100644
index 244bf285ea9b..000000000000
Binary files a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyzEditorTextWhenAutoSizeDisabled.png and /dev/null differ
diff --git a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyzEditorTextWhenAutoSizeTextChangesSet.png b/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyzEditorTextWhenAutoSizeTextChangesSet.png
deleted file mode 100644
index 58d6b136c9ec..000000000000
Binary files a/src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/VerifyzEditorTextWhenAutoSizeTextChangesSet.png and /dev/null differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/ClearPlaceholderIconShouldHideWhenDisabled.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/ClearPlaceholderIconShouldHideWhenDisabled.png
new file mode 100644
index 000000000000..f98b0b38c5a4
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/ClearPlaceholderIconShouldHideWhenDisabled.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/FlyoutOverlayResizesOnRotation.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/FlyoutOverlayResizesOnRotation.png
index 304631b4a0a2..660080dcfbb4 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/FlyoutOverlayResizesOnRotation.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/FlyoutOverlayResizesOnRotation.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/FlyoutSelectedStateReflectsUpdatedDynamicResource.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/FlyoutSelectedStateReflectsUpdatedDynamicResource.png
new file mode 100644
index 000000000000..1dd48d7c8ef1
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/FlyoutSelectedStateReflectsUpdatedDynamicResource.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/IndicatorViewCircleShape.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/IndicatorViewCircleShape.png
new file mode 100644
index 000000000000..5e972dd58de2
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/IndicatorViewCircleShape.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/IndicatorViewSquareShape.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/IndicatorViewSquareShape.png
new file mode 100644
index 000000000000..55e9fbf2ae37
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/IndicatorViewSquareShape.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/ItemImageSourceShouldBeVisible.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/ItemImageSourceShouldBeVisible.png
index 26bb63ef6368..99eba00f5685 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/ItemImageSourceShouldBeVisible.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/ItemImageSourceShouldBeVisible.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/NavigationPageChildContentExtendsUnderFloatingTabBar.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/NavigationPageChildContentExtendsUnderFloatingTabBar.png
new file mode 100644
index 000000000000..ceeec21015f8
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/NavigationPageChildContentExtendsUnderFloatingTabBar.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/RadioButton_Checking_Default_Configuration_VerifyVisualState.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/RadioButton_Checking_Default_Configuration_VerifyVisualState.png
index 9870f9d74b4f..f99343067736 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/RadioButton_Checking_Default_Configuration_VerifyVisualState.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/RadioButton_Checking_Default_Configuration_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/RadioButton_Checking_Initial_Configuration_VerifyVisualState.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/RadioButton_Checking_Initial_Configuration_VerifyVisualState.png
deleted file mode 100644
index 125e54a31384..000000000000
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/RadioButton_Checking_Initial_Configuration_VerifyVisualState.png and /dev/null differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/RadioButton_FlowDirectionAndContent_VerifyVisualState.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/RadioButton_FlowDirectionAndContent_VerifyVisualState.png
index 367a79a18e31..c3d46eeef806 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/RadioButton_FlowDirectionAndContent_VerifyVisualState.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/RadioButton_FlowDirectionAndContent_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/RadioButton_SetBorderWidthAndCornerRadius_VerifyVisualState.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/RadioButton_SetBorderWidthAndCornerRadius_VerifyVisualState.png
index 01526e0f5dd6..d1c3f2bd766c 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/RadioButton_SetBorderWidthAndCornerRadius_VerifyVisualState.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/RadioButton_SetBorderWidthAndCornerRadius_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/RadioButton_SetContentAndCharacterSpacing_VerifyVisualState.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/RadioButton_SetContentAndCharacterSpacing_VerifyVisualState.png
index 9532b6f370b3..406720cdb36e 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/RadioButton_SetContentAndCharacterSpacing_VerifyVisualState.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/RadioButton_SetContentAndCharacterSpacing_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/RadioButton_SetContentAndFontAttributes_VerifyVisualState.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/RadioButton_SetContentAndFontAttributes_VerifyVisualState.png
index bb4212c8adad..7abbef743fc0 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/RadioButton_SetContentAndFontAttributes_VerifyVisualState.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/RadioButton_SetContentAndFontAttributes_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/RadioButton_SetContentAndFontSize_VerifyVisualState.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/RadioButton_SetContentAndFontSize_VerifyVisualState.png
index bb8d4f2009d8..09c64fc62f8d 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/RadioButton_SetContentAndFontSize_VerifyVisualState.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/RadioButton_SetContentAndFontSize_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/RadioButton_SetContentAndTextColor_VerifyVisualState.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/RadioButton_SetContentAndTextColor_VerifyVisualState.png
index 9358792b1e42..700cd87bafa1 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/RadioButton_SetContentAndTextColor_VerifyVisualState.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/RadioButton_SetContentAndTextColor_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/RadioButton_SetContentAndTextTransform.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/RadioButton_SetContentAndTextTransform.png
index 061cc520e3e7..d68f36df721a 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/RadioButton_SetContentAndTextTransform.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/RadioButton_SetContentAndTextTransform.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/RadioButton_SetContentWithView.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/RadioButton_SetContentWithView.png
index 9fd80177605b..c1ea40800b87 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/RadioButton_SetContentWithView.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/RadioButton_SetContentWithView.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/RadioButton_SetFontAttributesAndTextColor_VerifyVisualState.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/RadioButton_SetFontAttributesAndTextColor_VerifyVisualState.png
index ffce0e302bb9..650a3abf7be6 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/RadioButton_SetFontAttributesAndTextColor_VerifyVisualState.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/RadioButton_SetFontAttributesAndTextColor_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/RadioButton_SetFontAutoScalingEnabled_VerifyVisualState.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/RadioButton_SetFontAutoScalingEnabled_VerifyVisualState.png
new file mode 100644
index 000000000000..0165b41549e0
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/RadioButton_SetFontAutoScalingEnabled_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/RadioButton_SetFontFamilyAndFontAttributes_VerifyVisualState.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/RadioButton_SetFontFamilyAndFontAttributes_VerifyVisualState.png
index 8be47835bf73..4a8b0c639329 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/RadioButton_SetFontFamilyAndFontAttributes_VerifyVisualState.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/RadioButton_SetFontFamilyAndFontAttributes_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/RadioButton_SetFontFamilyAndFontSize_VerifyVisualState.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/RadioButton_SetFontFamilyAndFontSize_VerifyVisualState.png
index 7c891e02a8aa..5edd682dbddb 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/RadioButton_SetFontFamilyAndFontSize_VerifyVisualState.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/RadioButton_SetFontFamilyAndFontSize_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/RadioButton_SetFontFamilyAndTextTransform_VerifyVisualState.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/RadioButton_SetFontFamilyAndTextTransform_VerifyVisualState.png
index 21582b74137c..7c272ea4d743 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/RadioButton_SetFontFamilyAndTextTransform_VerifyVisualState.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/RadioButton_SetFontFamilyAndTextTransform_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/RadioButton_SetFontSizeAndFontAttributes_VerifyVisualState.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/RadioButton_SetFontSizeAndFontAttributes_VerifyVisualState.png
index 9ca5a4744f3f..81b9eaeac7d3 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/RadioButton_SetFontSizeAndFontAttributes_VerifyVisualState.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/RadioButton_SetFontSizeAndFontAttributes_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/RadioButton_SetTextColorAndBorderColor_VerifyVisualState.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/RadioButton_SetTextColorAndBorderColor_VerifyVisualState.png
index 327c7d1f773d..72108621b1df 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/RadioButton_SetTextColorAndBorderColor_VerifyVisualState.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/RadioButton_SetTextColorAndBorderColor_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/SearchBarClearButtonShouldBeVisibleWithText.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/SearchBarClearButtonShouldBeVisibleWithText.png
new file mode 100644
index 000000000000..e827ccb265e5
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/SearchBarClearButtonShouldBeVisibleWithText.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/SearchBarClearButtonShouldDisappearAfterClearingInput.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/SearchBarClearButtonShouldDisappearAfterClearingInput.png
new file mode 100644
index 000000000000..054536ce5870
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/SearchBarClearButtonShouldDisappearAfterClearingInput.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/SearchHandlerClearIconUpdatesAtRuntime.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/SearchHandlerClearIconUpdatesAtRuntime.png
new file mode 100644
index 000000000000..360d20d85d29
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/SearchHandlerClearIconUpdatesAtRuntime.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/SearchHandlerClearPlaceholderIconUpdatesAtRuntime.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/SearchHandlerClearPlaceholderIconUpdatesAtRuntime.png
new file mode 100644
index 000000000000..479b5dc3ba21
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/SearchHandlerClearPlaceholderIconUpdatesAtRuntime.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/SearchHandlerQueryIconUpdatesAtRuntime.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/SearchHandlerQueryIconUpdatesAtRuntime.png
new file mode 100644
index 000000000000..1439ef1ab729
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/SearchHandlerQueryIconUpdatesAtRuntime.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/SearchHandlerResetAllRestoresDefaultIcons.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/SearchHandlerResetAllRestoresDefaultIcons.png
new file mode 100644
index 000000000000..33830ebc25bf
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/SearchHandlerResetAllRestoresDefaultIcons.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/SwipeItemFontAndSvgIconsRenderCorrectly.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/SwipeItemFontAndSvgIconsRenderCorrectly.png
deleted file mode 100644
index da1525b0eb44..000000000000
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/SwipeItemFontAndSvgIconsRenderCorrectly.png and /dev/null differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/TabTitlesShouldNotBeTruncated.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/TabTitlesShouldNotBeTruncated.png
deleted file mode 100644
index a1cfbb954990..000000000000
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/TabTitlesShouldNotBeTruncated.png and /dev/null differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/TappingDescendantInsideShadowedBorderShouldUpdateBackgroundColor.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/TappingDescendantInsideShadowedBorderShouldUpdateBackgroundColor.png
new file mode 100644
index 000000000000..f2c20dc226fc
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/TappingDescendantInsideShadowedBorderShouldUpdateBackgroundColor.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/TransparentShellNavBarShouldRemainTransparentAfterKeyboardDismiss.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/TransparentShellNavBarShouldRemainTransparentAfterKeyboardDismiss.png
new file mode 100644
index 000000000000..76246610eb56
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/TransparentShellNavBarShouldRemainTransparentAfterKeyboardDismiss.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyAnchorXAndAnchorYShadow.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyAnchorXAndAnchorYShadow.png
new file mode 100644
index 000000000000..2386eb5ae2ce
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyAnchorXAndAnchorYShadow.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyAnchorXAndShadow.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyAnchorXAndShadow.png
new file mode 100644
index 000000000000..7ac01665237e
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyAnchorXAndShadow.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyAnchorYAndShadow.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyAnchorYAndShadow.png
new file mode 100644
index 000000000000..dca5f3450b2d
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyAnchorYAndShadow.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyBorderWithNullStrokeDashArray.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyBorderWithNullStrokeDashArray.png
new file mode 100644
index 000000000000..f3e5efda936b
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyBorderWithNullStrokeDashArray.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyBorderWithStrokeDashArrayValue.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyBorderWithStrokeDashArrayValue.png
new file mode 100644
index 000000000000..eb464057c89c
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyBorderWithStrokeDashArrayValue.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyCarouselViewKeepScrollOffsetAdd.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyCarouselViewKeepScrollOffsetAdd.png
new file mode 100644
index 000000000000..07df09c0518d
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyCarouselViewKeepScrollOffsetAdd.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyCollectionViewContentWithButtonSwipeItem.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyCollectionViewContentWithButtonSwipeItem.png
index 793e8451606d..722ebdcba7e6 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyCollectionViewContentWithButtonSwipeItem.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyCollectionViewContentWithButtonSwipeItem.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyCollectionViewContentWithIconImageSwipeItem.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyCollectionViewContentWithIconImageSwipeItem.png
index c4687b324a66..8f3ee31b5b90 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyCollectionViewContentWithIconImageSwipeItem.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyCollectionViewContentWithIconImageSwipeItem.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyCollectionViewTextShouldAppearAfterRotatingTheDevice.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyCollectionViewTextShouldAppearAfterRotatingTheDevice.png
new file mode 100644
index 000000000000..4ce298e125e6
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyCollectionViewTextShouldAppearAfterRotatingTheDevice.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyCustomFlyoutContentRendering.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyCustomFlyoutContentRendering.png
new file mode 100644
index 000000000000..a4ffd4cd3e33
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyCustomFlyoutContentRendering.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyCustomFlyoutContentTemplateRendering.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyCustomFlyoutContentTemplateRendering.png
new file mode 100644
index 000000000000..3423fa125834
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyCustomFlyoutContentTemplateRendering.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyCustomFlyoutContentTemplateWithHeaderFooter.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyCustomFlyoutContentTemplateWithHeaderFooter.png
new file mode 100644
index 000000000000..d711580bb3fb
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyCustomFlyoutContentTemplateWithHeaderFooter.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyCustomFlyoutContentWithHeaderFooter.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyCustomFlyoutContentWithHeaderFooter.png
new file mode 100644
index 000000000000..b5a90dea88ca
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyCustomFlyoutContentWithHeaderFooter.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyDefaultFlyoutItemsRendering.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyDefaultFlyoutItemsRendering.png
new file mode 100644
index 000000000000..0fae8fd95c8a
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyDefaultFlyoutItemsRendering.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyDefaultScrollToRequested.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyDefaultScrollToRequested.png
deleted file mode 100644
index 20806430a1f4..000000000000
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyDefaultScrollToRequested.png and /dev/null differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorBackgroundColorWithPlaceholder.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorBackgroundColorWithPlaceholder.png
new file mode 100644
index 000000000000..2369b60ded1f
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorBackgroundColorWithPlaceholder.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorBackgroundColorWithTextColor.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorBackgroundColorWithTextColor.png
new file mode 100644
index 000000000000..b5d56fa9a5fb
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorBackgroundColorWithTextColor.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorCharacterSpacingWhenFontFamily.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorCharacterSpacingWhenFontFamily.png
index cad3f2674a79..905dd900e39f 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorCharacterSpacingWhenFontFamily.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorCharacterSpacingWhenFontFamily.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorCharacterSpacingWhenMaxLengthSet.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorCharacterSpacingWhenMaxLengthSet.png
index 1f0dbc5f486e..b52ff564c265 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorCharacterSpacingWhenMaxLengthSet.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorCharacterSpacingWhenMaxLengthSet.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorControlWhenFlowDirectionSet.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorControlWhenFlowDirectionSet.png
index 8b109aed84df..37d2b2d7b2f8 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorControlWhenFlowDirectionSet.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorControlWhenFlowDirectionSet.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorControlWhenPlaceholderColorSet.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorControlWhenPlaceholderColorSet.png
index cd70e28ca3a6..017b82ba8f2f 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorControlWhenPlaceholderColorSet.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorControlWhenPlaceholderColorSet.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorControlWhenPlaceholderColorSetDefaultValue.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorControlWhenPlaceholderColorSetDefaultValue.png
new file mode 100644
index 000000000000..3148675b5155
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorControlWhenPlaceholderColorSetDefaultValue.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorControlWhenPlaceholderTextSet.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorControlWhenPlaceholderTextSet.png
index 4294945091fe..d325571627f4 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorControlWhenPlaceholderTextSet.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorControlWhenPlaceholderTextSet.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorHorizontalTextAlignmentBasedOnCharacterSpacing.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorHorizontalTextAlignmentBasedOnCharacterSpacing.png
index 7d6222805351..cf5023e8ac64 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorHorizontalTextAlignmentBasedOnCharacterSpacing.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorHorizontalTextAlignmentBasedOnCharacterSpacing.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorHorizontalTextAlignmentWhenVerticalTextAlignmentSet.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorHorizontalTextAlignmentWhenVerticalTextAlignmentSet.png
index 388417317a7c..96af095f060f 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorHorizontalTextAlignmentWhenVerticalTextAlignmentSet.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorHorizontalTextAlignmentWhenVerticalTextAlignmentSet.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorPlaceholderTextWhenFontAttributesBoldAndItalicSet.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorPlaceholderTextWhenFontAttributesBoldAndItalicSet.png
new file mode 100644
index 000000000000..f9564a32d6c4
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorPlaceholderTextWhenFontAttributesBoldAndItalicSet.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorPlaceholderWhenFlowDirectionSet.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorPlaceholderWhenFlowDirectionSet.png
index 6930c09a2ac0..9fe8f1902005 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorPlaceholderWhenFlowDirectionSet.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorPlaceholderWhenFlowDirectionSet.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorPlaceholderWithCharacterSpacing.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorPlaceholderWithCharacterSpacing.png
index cb22a7d7abae..7900fda40e19 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorPlaceholderWithCharacterSpacing.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorPlaceholderWithCharacterSpacing.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorPlaceholderWithFontAttributes.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorPlaceholderWithFontAttributes.png
index 66cc45295a7c..812310e3c95d 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorPlaceholderWithFontAttributes.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorPlaceholderWithFontAttributes.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorPlaceholderWithFontFamily.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorPlaceholderWithFontFamily.png
index 769f8a90d18b..cf4c54a24cab 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorPlaceholderWithFontFamily.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorPlaceholderWithFontFamily.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorPlaceholderWithFontSize.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorPlaceholderWithFontSize.png
index 8766a1521278..640416a625c4 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorPlaceholderWithFontSize.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorPlaceholderWithFontSize.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorPlaceholderWithHorizontalAlignment.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorPlaceholderWithHorizontalAlignment.png
index 47de0668ba9b..52c869162360 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorPlaceholderWithHorizontalAlignment.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorPlaceholderWithHorizontalAlignment.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorPlaceholderWithShadow.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorPlaceholderWithShadow.png
index 0674ad0c4e46..6796a1087b55 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorPlaceholderWithShadow.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorPlaceholderWithShadow.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorPlaceholderWithVerticalAlignment.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorPlaceholderWithVerticalAlignment.png
index 0dfd56ae6a85..bd93a3ec891c 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorPlaceholderWithVerticalAlignment.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorPlaceholderWithVerticalAlignment.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorTextColorSetDefaultValue.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorTextColorSetDefaultValue.png
new file mode 100644
index 000000000000..ac41be398824
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorTextColorSetDefaultValue.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorTextWhenAlignedHorizontally.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorTextWhenAlignedHorizontally.png
new file mode 100644
index 000000000000..780f0afe6a86
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorTextWhenAlignedHorizontally.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorTextWhenAlignedVertically.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorTextWhenAlignedVertically.png
new file mode 100644
index 000000000000..9bf923c0c67b
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorTextWhenAlignedVertically.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorTextWhenAlingnedHorizontally.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorTextWhenAlingnedHorizontally.png
deleted file mode 100644
index 1213b11e1c2f..000000000000
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorTextWhenAlingnedHorizontally.png and /dev/null differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyzEditorTextWhenAutoSizeDisabled.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorTextWhenAutoSizeDisabled.png
similarity index 98%
rename from src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyzEditorTextWhenAutoSizeDisabled.png
rename to src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorTextWhenAutoSizeDisabled.png
index 3a7cb6217f49..61ba12c18a89 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyzEditorTextWhenAutoSizeDisabled.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorTextWhenAutoSizeDisabled.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorTextWhenAutoSizeTextChangesSet.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorTextWhenAutoSizeTextChangesSet.png
new file mode 100644
index 000000000000..f88c89321bb4
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorTextWhenAutoSizeTextChangesSet.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorTextWhenAutoSizeTextChangesSetWithHeightRequest.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorTextWhenAutoSizeTextChangesSetWithHeightRequest.png
new file mode 100644
index 000000000000..9fccada3381c
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorTextWhenAutoSizeTextChangesSetWithHeightRequest.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyzEditorTextWhenAutoSizeTextChangesSet.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorTextWhenAutoSizeTextChangesSetWithShortShrinkText_LongText.png
similarity index 99%
rename from src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyzEditorTextWhenAutoSizeTextChangesSet.png
rename to src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorTextWhenAutoSizeTextChangesSetWithShortShrinkText_LongText.png
index 7b855060c0d3..b44f7c457620 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyzEditorTextWhenAutoSizeTextChangesSet.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorTextWhenAutoSizeTextChangesSetWithShortShrinkText_LongText.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorTextWhenAutoSizeTextChangesSetWithShortShrinkText_ShortText.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorTextWhenAutoSizeTextChangesSetWithShortShrinkText_ShortText.png
new file mode 100644
index 000000000000..eb38a55b67e2
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorTextWhenAutoSizeTextChangesSetWithShortShrinkText_ShortText.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorTextWhenCharacterSpacingSetValues.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorTextWhenCharacterSpacingSetValues.png
index ca30cdacdc9a..9e65912b1506 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorTextWhenCharacterSpacingSetValues.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorTextWhenCharacterSpacingSetValues.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorTextWhenFontAttributesBoldAndItalicSet.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorTextWhenFontAttributesBoldAndItalicSet.png
new file mode 100644
index 000000000000..9bbec587d208
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorTextWhenFontAttributesBoldAndItalicSet.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorTextWhenFontAttributesSet.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorTextWhenFontAttributesSet.png
index 333cd60059fc..6f3b0d3d0ed0 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorTextWhenFontAttributesSet.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorTextWhenFontAttributesSet.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorTextWhenFontFamilySetValue.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorTextWhenFontFamilySetValue.png
index d846d8f70844..13b3a928519d 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorTextWhenFontFamilySetValue.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorTextWhenFontFamilySetValue.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorTextWhenFontSizeSetCorrectly.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorTextWhenFontSizeSetCorrectly.png
index 3dde678d15fd..cfe9a6e58317 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorTextWhenFontSizeSetCorrectly.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorTextWhenFontSizeSetCorrectly.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorTextWhenTextColorSetCorrectly.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorTextWhenTextColorSetCorrectly.png
index f0e516df01e6..704f5a87bba9 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorTextWhenTextColorSetCorrectly.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorTextWhenTextColorSetCorrectly.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorVerticalTextAlignmentBasedOnCharacterSpacing.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorVerticalTextAlignmentBasedOnCharacterSpacing.png
index 4dfec5cd837b..b2d332ce81fa 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorVerticalTextAlignmentBasedOnCharacterSpacing.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorVerticalTextAlignmentBasedOnCharacterSpacing.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorWhenBackgroundColorSet.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorWhenBackgroundColorSet.png
new file mode 100644
index 000000000000..b8fce9a13a1f
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorWhenBackgroundColorSet.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorWhenHeightAndWidthRequestSet.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorWhenHeightAndWidthRequestSet.png
new file mode 100644
index 000000000000..50cfa97eb067
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorWhenHeightAndWidthRequestSet.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorTextWhenAlingnedVertically.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorWhenHeightRequestSet.png
similarity index 97%
rename from src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorTextWhenAlingnedVertically.png
rename to src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorWhenHeightRequestSet.png
index b488744fe420..a7ad4e0feed8 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorTextWhenAlingnedVertically.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorWhenHeightRequestSet.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorWhenOpacityResetToDefault.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorWhenOpacityResetToDefault.png
new file mode 100644
index 000000000000..6afd143283e3
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorWhenOpacityResetToDefault.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorWhenOpacitySet.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorWhenOpacitySet.png
new file mode 100644
index 000000000000..b5b48e951af1
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorWhenOpacitySet.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorWhenOpacitySetToZero.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorWhenOpacitySetToZero.png
new file mode 100644
index 000000000000..e937eda5c301
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorWhenOpacitySetToZero.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorWhenWidthRequestSet.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorWhenWidthRequestSet.png
new file mode 100644
index 000000000000..196842cce3ea
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorWhenWidthRequestSet.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorWithShadow.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorWithShadow.png
new file mode 100644
index 000000000000..d3082eeeafdf
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditorWithShadow.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditor_WithShadow.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditor_WithShadow.png
deleted file mode 100644
index 595f97ffe288..000000000000
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyEditor_WithShadow.png and /dev/null differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyFlyoutWithHeaderFooter.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyFlyoutWithHeaderFooter.png
new file mode 100644
index 000000000000..e8fa0c3db6ad
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyFlyoutWithHeaderFooter.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyRotationAndShadow.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyRotationAndShadow.png
new file mode 100644
index 000000000000..acb14557ea9e
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyRotationAndShadow.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyRotationXAndShadow.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyRotationXAndShadow.png
new file mode 100644
index 000000000000..797fc2677a21
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyRotationXAndShadow.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyRotationYAndShadow.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyRotationYAndShadow.png
new file mode 100644
index 000000000000..bf54a3183e96
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyRotationYAndShadow.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyScaleAndShadow.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyScaleAndShadow.png
new file mode 100644
index 000000000000..7e786841ab52
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyScaleAndShadow.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyScaleXAndShadow.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyScaleXAndShadow.png
new file mode 100644
index 000000000000..6b005b706159
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyScaleXAndShadow.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyScaleYAndShadow.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyScaleYAndShadow.png
new file mode 100644
index 000000000000..f58ca26300c8
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyScaleYAndShadow.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyScrollViewDirection.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyScrollViewDirection.png
new file mode 100644
index 000000000000..e49d58992d1a
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyScrollViewDirection.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyScrollViewHeightWhenRemoveChildAtRuntime.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyScrollViewHeightWhenRemoveChildAtRuntime.png
new file mode 100644
index 000000000000..3a5705b605a4
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyScrollViewHeightWhenRemoveChildAtRuntime.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyShellFlyout_Height.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyShellFlyout_Height.png
index 9ec2a396541b..42b036be6c3d 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyShellFlyout_Height.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyShellFlyout_Height.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyShellFlyout_HeightAndWidthWithBackgroundColor.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyShellFlyout_HeightAndWidthWithBackgroundColor.png
index 2804b8567467..dd405133d508 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyShellFlyout_HeightAndWidthWithBackgroundColor.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyShellFlyout_HeightAndWidthWithBackgroundColor.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyShellFlyout_HeightAndWidthWithBackgroundImage.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyShellFlyout_HeightAndWidthWithBackgroundImage.png
index 18d87a1fd610..4a89cddb8dd0 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyShellFlyout_HeightAndWidthWithBackgroundImage.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyShellFlyout_HeightAndWidthWithBackgroundImage.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifySwipeViewApperance.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifySwipeViewApperance.png
index d23f0f8c446a..6dd0af8f2463 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifySwipeViewApperance.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifySwipeViewApperance.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifySwipeViewWithButtonSwipeItemsBackgroundColor.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifySwipeViewWithButtonSwipeItemsBackgroundColor.png
index 5e3864492a58..97d4746e7d9b 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifySwipeViewWithButtonSwipeItemsBackgroundColor.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifySwipeViewWithButtonSwipeItemsBackgroundColor.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifySwipeViewWithCollectionViewContentAndThreshold.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifySwipeViewWithCollectionViewContentAndThreshold.png
index fcc6aebb10b6..eedd5bb33fb3 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifySwipeViewWithCollectionViewContentAndThreshold.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifySwipeViewWithCollectionViewContentAndThreshold.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifySwipeViewWithImageContentAndThreshold.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifySwipeViewWithImageContentAndThreshold.png
index 8759edf01fdd..2197dcf5e6a7 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifySwipeViewWithImageContentAndThreshold.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifySwipeViewWithImageContentAndThreshold.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifySwipeViewWithLabelContentAndThreshold.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifySwipeViewWithLabelContentAndThreshold.png
index d1b7284cc591..ff1f02decac4 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifySwipeViewWithLabelContentAndThreshold.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifySwipeViewWithLabelContentAndThreshold.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyTranslationXAndShadow.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyTranslationXAndShadow.png
new file mode 100644
index 000000000000..d70ee99b51b6
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyTranslationXAndShadow.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyTranslationYAndShadow.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyTranslationYAndShadow.png
new file mode 100644
index 000000000000..b8b9b49d8395
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/VerifyTranslationYAndShadow.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/ClearPlaceholderIconShouldHideWhenDisabled.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/ClearPlaceholderIconShouldHideWhenDisabled.png
new file mode 100644
index 000000000000..3669e5bc4273
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/ClearPlaceholderIconShouldHideWhenDisabled.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/FlyoutOverlayResizesOnRotation.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/FlyoutOverlayResizesOnRotation.png
index 304631b4a0a2..d24da167849f 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/FlyoutOverlayResizesOnRotation.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/FlyoutOverlayResizesOnRotation.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/FlyoutSelectedStateReflectsUpdatedDynamicResource.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/FlyoutSelectedStateReflectsUpdatedDynamicResource.png
new file mode 100644
index 000000000000..3714f228f4d4
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/FlyoutSelectedStateReflectsUpdatedDynamicResource.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/IndicatorViewCircleShape.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/IndicatorViewCircleShape.png
new file mode 100644
index 000000000000..afd0c35ad191
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/IndicatorViewCircleShape.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/IndicatorViewSquareShape.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/IndicatorViewSquareShape.png
new file mode 100644
index 000000000000..d9e625f1e13e
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/IndicatorViewSquareShape.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/ItemImageSourceShouldBeVisible.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/ItemImageSourceShouldBeVisible.png
index df4f876a7e4d..444a027e1be8 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/ItemImageSourceShouldBeVisible.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/ItemImageSourceShouldBeVisible.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/NavigationPageChildContentExtendsUnderFloatingTabBar.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/NavigationPageChildContentExtendsUnderFloatingTabBar.png
new file mode 100644
index 000000000000..67199599de24
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/NavigationPageChildContentExtendsUnderFloatingTabBar.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/RadioButton_Checking_Initial_Configuration_VerifyVisualState.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/RadioButton_Checking_Initial_Configuration_VerifyVisualState.png
deleted file mode 100644
index 9961b2289825..000000000000
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/RadioButton_Checking_Initial_Configuration_VerifyVisualState.png and /dev/null differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/RadioButton_SetFontAutoScalingEnabled_VerifyVisualState.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/RadioButton_SetFontAutoScalingEnabled_VerifyVisualState.png
new file mode 100644
index 000000000000..7c03a8c67d60
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/RadioButton_SetFontAutoScalingEnabled_VerifyVisualState.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/SearchBarClearButtonShouldBeVisibleWithText.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/SearchBarClearButtonShouldBeVisibleWithText.png
new file mode 100644
index 000000000000..220cf69661ee
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/SearchBarClearButtonShouldBeVisibleWithText.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/SearchBarClearButtonShouldDisappearAfterClearingInput.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/SearchBarClearButtonShouldDisappearAfterClearingInput.png
new file mode 100644
index 000000000000..bc8bc4e23c24
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/SearchBarClearButtonShouldDisappearAfterClearingInput.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/SearchHandlerClearIconUpdatesAtRuntime.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/SearchHandlerClearIconUpdatesAtRuntime.png
new file mode 100644
index 000000000000..35d72f985a92
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/SearchHandlerClearIconUpdatesAtRuntime.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/SearchHandlerClearPlaceholderIconUpdatesAtRuntime.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/SearchHandlerClearPlaceholderIconUpdatesAtRuntime.png
new file mode 100644
index 000000000000..3d44aba86779
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/SearchHandlerClearPlaceholderIconUpdatesAtRuntime.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/SearchHandlerQueryIconUpdatesAtRuntime.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/SearchHandlerQueryIconUpdatesAtRuntime.png
new file mode 100644
index 000000000000..3113ae84fe39
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/SearchHandlerQueryIconUpdatesAtRuntime.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/SearchHandlerResetAllRestoresDefaultIcons.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/SearchHandlerResetAllRestoresDefaultIcons.png
new file mode 100644
index 000000000000..803b3024c4a1
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/SearchHandlerResetAllRestoresDefaultIcons.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/SwipeItemFontAndSvgIconsRenderCorrectly.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/SwipeItemFontAndSvgIconsRenderCorrectly.png
deleted file mode 100644
index da1525b0eb44..000000000000
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/SwipeItemFontAndSvgIconsRenderCorrectly.png and /dev/null differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/TabTitlesShouldNotBeTruncated.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/TabTitlesShouldNotBeTruncated.png
deleted file mode 100644
index 312722448cad..000000000000
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/TabTitlesShouldNotBeTruncated.png and /dev/null differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/TappingDescendantInsideShadowedBorderShouldUpdateBackgroundColor.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/TappingDescendantInsideShadowedBorderShouldUpdateBackgroundColor.png
new file mode 100644
index 000000000000..4822d781bd6e
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/TappingDescendantInsideShadowedBorderShouldUpdateBackgroundColor.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/TransparentShellNavBarShouldRemainTransparentAfterKeyboardDismiss.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/TransparentShellNavBarShouldRemainTransparentAfterKeyboardDismiss.png
new file mode 100644
index 000000000000..5623e5b84438
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/TransparentShellNavBarShouldRemainTransparentAfterKeyboardDismiss.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyAnchorXAndAnchorYShadow.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyAnchorXAndAnchorYShadow.png
new file mode 100644
index 000000000000..8d1c995e1bc7
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyAnchorXAndAnchorYShadow.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyAnchorXAndShadow.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyAnchorXAndShadow.png
new file mode 100644
index 000000000000..87a9de6e8794
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyAnchorXAndShadow.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyAnchorYAndShadow.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyAnchorYAndShadow.png
new file mode 100644
index 000000000000..486b595a6df6
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyAnchorYAndShadow.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyBorderWithNullStrokeDashArray.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyBorderWithNullStrokeDashArray.png
new file mode 100644
index 000000000000..994c64ffb3ad
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyBorderWithNullStrokeDashArray.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyBorderWithStrokeDashArrayValue.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyBorderWithStrokeDashArrayValue.png
new file mode 100644
index 000000000000..0950f438f5de
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyBorderWithStrokeDashArrayValue.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyCarouselScrollsToEndItemAfterReset.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyCarouselScrollsToEndItemAfterReset.png
new file mode 100644
index 000000000000..1fa29dcd43e8
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyCarouselScrollsToEndItemAfterReset.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyCarouselViewKeepScrollOffsetAdd.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyCarouselViewKeepScrollOffsetAdd.png
new file mode 100644
index 000000000000..b0c5ca480398
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyCarouselViewKeepScrollOffsetAdd.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyCollectionViewContentWithButtonSwipeItem.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyCollectionViewContentWithButtonSwipeItem.png
index f80cbf9742bd..88eb71c6aff1 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyCollectionViewContentWithButtonSwipeItem.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyCollectionViewContentWithButtonSwipeItem.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyCollectionViewContentWithIconImageSwipeItem.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyCollectionViewContentWithIconImageSwipeItem.png
index cb24014bac78..e2496441beb5 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyCollectionViewContentWithIconImageSwipeItem.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyCollectionViewContentWithIconImageSwipeItem.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyCollectionViewTextShouldAppearAfterRotatingTheDevice.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyCollectionViewTextShouldAppearAfterRotatingTheDevice.png
new file mode 100644
index 000000000000..8192e595290a
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyCollectionViewTextShouldAppearAfterRotatingTheDevice.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyCustomFlyoutContentRendering.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyCustomFlyoutContentRendering.png
new file mode 100644
index 000000000000..cfae95a6decc
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyCustomFlyoutContentRendering.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyCustomFlyoutContentTemplateRendering.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyCustomFlyoutContentTemplateRendering.png
new file mode 100644
index 000000000000..616d315a72cc
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyCustomFlyoutContentTemplateRendering.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyCustomFlyoutContentTemplateWithHeaderFooter.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyCustomFlyoutContentTemplateWithHeaderFooter.png
new file mode 100644
index 000000000000..6413d93e8cb9
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyCustomFlyoutContentTemplateWithHeaderFooter.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyCustomFlyoutContentWithHeaderFooter.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyCustomFlyoutContentWithHeaderFooter.png
new file mode 100644
index 000000000000..f66f9a27a57a
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyCustomFlyoutContentWithHeaderFooter.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyDefaultFlyoutItemsRendering.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyDefaultFlyoutItemsRendering.png
new file mode 100644
index 000000000000..cc55744f9f87
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyDefaultFlyoutItemsRendering.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyDefaultScrollToRequested.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyDefaultScrollToRequested.png
deleted file mode 100644
index a1ee3016f99e..000000000000
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyDefaultScrollToRequested.png and /dev/null differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyEditorBackgroundColorWithPlaceholder.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyEditorBackgroundColorWithPlaceholder.png
new file mode 100644
index 000000000000..b57496583e8d
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyEditorBackgroundColorWithPlaceholder.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyEditorBackgroundColorWithTextColor.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyEditorBackgroundColorWithTextColor.png
new file mode 100644
index 000000000000..617d23936e88
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyEditorBackgroundColorWithTextColor.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyEditorControlWhenPlaceholderColorSetDefaultValue.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyEditorControlWhenPlaceholderColorSetDefaultValue.png
new file mode 100644
index 000000000000..3609c038abd1
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyEditorControlWhenPlaceholderColorSetDefaultValue.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyEditorControlWhenPlaceholderTextSet.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyEditorControlWhenPlaceholderTextSet.png
index 32a9548743a5..219fcbad9e40 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyEditorControlWhenPlaceholderTextSet.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyEditorControlWhenPlaceholderTextSet.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyEditorHorizontalTextAlignmentWhenVerticalTextAlignmentSet.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyEditorHorizontalTextAlignmentWhenVerticalTextAlignmentSet.png
index 496ff68e7713..474acdfcd51b 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyEditorHorizontalTextAlignmentWhenVerticalTextAlignmentSet.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyEditorHorizontalTextAlignmentWhenVerticalTextAlignmentSet.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyEditorPlaceholderTextWhenFontAttributesBoldAndItalicSet.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyEditorPlaceholderTextWhenFontAttributesBoldAndItalicSet.png
new file mode 100644
index 000000000000..48b4f2fa4e3f
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyEditorPlaceholderTextWhenFontAttributesBoldAndItalicSet.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyEditorPlaceholderWhenFlowDirectionSet.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyEditorPlaceholderWhenFlowDirectionSet.png
index 5cb09846e720..cbf600ff573f 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyEditorPlaceholderWhenFlowDirectionSet.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyEditorPlaceholderWhenFlowDirectionSet.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyEditorPlaceholderWithCharacterSpacing.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyEditorPlaceholderWithCharacterSpacing.png
index 90c1ce444cce..498fd21c8a6f 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyEditorPlaceholderWithCharacterSpacing.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyEditorPlaceholderWithCharacterSpacing.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyEditorPlaceholderWithFontAttributes.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyEditorPlaceholderWithFontAttributes.png
index ff6721184aae..2f15e21ad5f1 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyEditorPlaceholderWithFontAttributes.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyEditorPlaceholderWithFontAttributes.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyEditorPlaceholderWithFontFamily.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyEditorPlaceholderWithFontFamily.png
index c37edd3ee3e6..6caa60708853 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyEditorPlaceholderWithFontFamily.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyEditorPlaceholderWithFontFamily.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyEditorPlaceholderWithFontSize.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyEditorPlaceholderWithFontSize.png
index 3535edde0670..c3ff202ee330 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyEditorPlaceholderWithFontSize.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyEditorPlaceholderWithFontSize.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyEditorPlaceholderWithHorizontalAlignment.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyEditorPlaceholderWithHorizontalAlignment.png
index f0724ba00773..24eeee7feae8 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyEditorPlaceholderWithHorizontalAlignment.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyEditorPlaceholderWithHorizontalAlignment.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyEditorPlaceholderWithShadow.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyEditorPlaceholderWithShadow.png
index 50f4efb9f9e9..24e76fb411bd 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyEditorPlaceholderWithShadow.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyEditorPlaceholderWithShadow.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyEditorPlaceholderWithVerticalAlignment.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyEditorPlaceholderWithVerticalAlignment.png
index afe8a1468ced..4b2ce02a8526 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyEditorPlaceholderWithVerticalAlignment.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyEditorPlaceholderWithVerticalAlignment.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyEditorTextColorSetDefaultValue.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyEditorTextColorSetDefaultValue.png
new file mode 100644
index 000000000000..198266f80b4d
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyEditorTextColorSetDefaultValue.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyEditorTextWhenAlignedHorizontally.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyEditorTextWhenAlignedHorizontally.png
new file mode 100644
index 000000000000..38dedc684d4a
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyEditorTextWhenAlignedHorizontally.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyEditorTextWhenAlignedVertically.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyEditorTextWhenAlignedVertically.png
new file mode 100644
index 000000000000..8f3e50e8f978
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyEditorTextWhenAlignedVertically.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyEditorTextWhenAlingnedHorizontally.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyEditorTextWhenAlingnedHorizontally.png
deleted file mode 100644
index f94876a56888..000000000000
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyEditorTextWhenAlingnedHorizontally.png and /dev/null differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyzEditorTextWhenAutoSizeDisabled.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyEditorTextWhenAutoSizeDisabled.png
similarity index 97%
rename from src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyzEditorTextWhenAutoSizeDisabled.png
rename to src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyEditorTextWhenAutoSizeDisabled.png
index 9d9be9595c7c..c08ee0fa5406 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyzEditorTextWhenAutoSizeDisabled.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyEditorTextWhenAutoSizeDisabled.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyzEditorTextWhenAutoSizeTextChangesSet.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyEditorTextWhenAutoSizeTextChangesSet.png
similarity index 98%
rename from src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyzEditorTextWhenAutoSizeTextChangesSet.png
rename to src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyEditorTextWhenAutoSizeTextChangesSet.png
index 41a539593a9d..3c3010c0aa6e 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyzEditorTextWhenAutoSizeTextChangesSet.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyEditorTextWhenAutoSizeTextChangesSet.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyEditorTextWhenAutoSizeTextChangesSetWithHeightRequest.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyEditorTextWhenAutoSizeTextChangesSetWithHeightRequest.png
new file mode 100644
index 000000000000..b90f8bcdcccf
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyEditorTextWhenAutoSizeTextChangesSetWithHeightRequest.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyEditorTextWhenAutoSizeTextChangesSetWithShortShrinkText.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyEditorTextWhenAutoSizeTextChangesSetWithShortShrinkText.png
new file mode 100644
index 000000000000..b6d1f762f5f2
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyEditorTextWhenAutoSizeTextChangesSetWithShortShrinkText.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyEditorTextWhenAutoSizeTextChangesSetWithShortShrinkText_LongText.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyEditorTextWhenAutoSizeTextChangesSetWithShortShrinkText_LongText.png
new file mode 100644
index 000000000000..8363cbeeed1c
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyEditorTextWhenAutoSizeTextChangesSetWithShortShrinkText_LongText.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyEditorTextWhenAutoSizeTextChangesSetWithShortShrinkText_ShortText.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyEditorTextWhenAutoSizeTextChangesSetWithShortShrinkText_ShortText.png
new file mode 100644
index 000000000000..e8123de02816
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyEditorTextWhenAutoSizeTextChangesSetWithShortShrinkText_ShortText.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyEditorTextWhenFontAttributesBoldAndItalicSet.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyEditorTextWhenFontAttributesBoldAndItalicSet.png
new file mode 100644
index 000000000000..5c7cf30bd9da
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyEditorTextWhenFontAttributesBoldAndItalicSet.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyEditorTextWhenFontFamilySetValue.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyEditorTextWhenFontFamilySetValue.png
index 07d6522b0fa1..24b839143888 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyEditorTextWhenFontFamilySetValue.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyEditorTextWhenFontFamilySetValue.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyEditorWhenBackgroundColorSet.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyEditorWhenBackgroundColorSet.png
new file mode 100644
index 000000000000..c36180af8213
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyEditorWhenBackgroundColorSet.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyEditorWhenHeightAndWidthRequestSet.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyEditorWhenHeightAndWidthRequestSet.png
new file mode 100644
index 000000000000..482148f63c7a
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyEditorWhenHeightAndWidthRequestSet.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyEditorTextWhenAlingnedVertically.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyEditorWhenHeightRequestSet.png
similarity index 91%
rename from src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyEditorTextWhenAlingnedVertically.png
rename to src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyEditorWhenHeightRequestSet.png
index bc0af5c7faab..02288bc88d1f 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyEditorTextWhenAlingnedVertically.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyEditorWhenHeightRequestSet.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyEditorWhenOpacityResetToDefault.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyEditorWhenOpacityResetToDefault.png
new file mode 100644
index 000000000000..899e57dcab55
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyEditorWhenOpacityResetToDefault.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyEditorWhenOpacitySet.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyEditorWhenOpacitySet.png
new file mode 100644
index 000000000000..d1d4f830cdab
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyEditorWhenOpacitySet.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyEditorWhenOpacitySetToZero.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyEditorWhenOpacitySetToZero.png
new file mode 100644
index 000000000000..79b61f300c7c
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyEditorWhenOpacitySetToZero.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyEditorWhenWidthRequestSet.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyEditorWhenWidthRequestSet.png
new file mode 100644
index 000000000000..8f8ed324cf2e
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyEditorWhenWidthRequestSet.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyEditorWithShadow.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyEditorWithShadow.png
new file mode 100644
index 000000000000..0e175b0bd57e
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyEditorWithShadow.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyEditor_WithShadow.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyEditor_WithShadow.png
deleted file mode 100644
index af4a634705a7..000000000000
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyEditor_WithShadow.png and /dev/null differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyFlyoutWithHeaderFooter.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyFlyoutWithHeaderFooter.png
new file mode 100644
index 000000000000..4f4306749d48
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyFlyoutWithHeaderFooter.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyRotationAndShadow.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyRotationAndShadow.png
new file mode 100644
index 000000000000..1419c4acba7c
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyRotationAndShadow.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyRotationXAndShadow.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyRotationXAndShadow.png
new file mode 100644
index 000000000000..63cb88825184
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyRotationXAndShadow.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyRotationYAndShadow.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyRotationYAndShadow.png
new file mode 100644
index 000000000000..029e2c7c8de0
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyRotationYAndShadow.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyScaleAndShadow.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyScaleAndShadow.png
new file mode 100644
index 000000000000..309619c03a30
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyScaleAndShadow.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyScaleXAndShadow.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyScaleXAndShadow.png
new file mode 100644
index 000000000000..80e1eef981a3
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyScaleXAndShadow.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyScaleYAndShadow.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyScaleYAndShadow.png
new file mode 100644
index 000000000000..58ae67320a14
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyScaleYAndShadow.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyScrollViewDirection.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyScrollViewDirection.png
new file mode 100644
index 000000000000..bd041eaad83f
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyScrollViewDirection.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyScrollViewHeightWhenRemoveChildAtRuntime.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyScrollViewHeightWhenRemoveChildAtRuntime.png
new file mode 100644
index 000000000000..e68d6518da1f
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyScrollViewHeightWhenRemoveChildAtRuntime.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyShellFlyout_FlyoutItemVisibility.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyShellFlyout_FlyoutItemVisibility.png
index eb5ae5e60b9e..d93e5309f7e0 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyShellFlyout_FlyoutItemVisibility.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyShellFlyout_FlyoutItemVisibility.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyShellFlyout_Height.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyShellFlyout_Height.png
index d2dfd6990e44..3743a5a6d3c8 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyShellFlyout_Height.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyShellFlyout_Height.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyShellFlyout_HeightAndWidthWithBackgroundColor.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyShellFlyout_HeightAndWidthWithBackgroundColor.png
index 034f675533c7..2cd70cb3107b 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyShellFlyout_HeightAndWidthWithBackgroundColor.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyShellFlyout_HeightAndWidthWithBackgroundColor.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyShellFlyout_HeightAndWidthWithBackgroundImage.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyShellFlyout_HeightAndWidthWithBackgroundImage.png
index a3ae1c457e16..f304e9153d3b 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyShellFlyout_HeightAndWidthWithBackgroundImage.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyShellFlyout_HeightAndWidthWithBackgroundImage.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifySwipeViewApperance.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifySwipeViewApperance.png
index d23f0f8c446a..6dd0af8f2463 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifySwipeViewApperance.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifySwipeViewApperance.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifySwipeViewWithButtonSwipeItemsBackgroundColor.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifySwipeViewWithButtonSwipeItemsBackgroundColor.png
index ecb3e9bb97b1..7ca17ae7f026 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifySwipeViewWithButtonSwipeItemsBackgroundColor.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifySwipeViewWithButtonSwipeItemsBackgroundColor.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifySwipeViewWithCollectionViewContentAndThreshold.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifySwipeViewWithCollectionViewContentAndThreshold.png
index ce0ab9257f5a..0f856abb128a 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifySwipeViewWithCollectionViewContentAndThreshold.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifySwipeViewWithCollectionViewContentAndThreshold.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifySwipeViewWithImageContentAndThreshold.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifySwipeViewWithImageContentAndThreshold.png
index 2d68618d3796..815bd4afbb12 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifySwipeViewWithImageContentAndThreshold.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifySwipeViewWithImageContentAndThreshold.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifySwipeViewWithLabelContentAndThreshold.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifySwipeViewWithLabelContentAndThreshold.png
index e8ce0b679d62..a49b73f4c1a6 100644
Binary files a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifySwipeViewWithLabelContentAndThreshold.png and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifySwipeViewWithLabelContentAndThreshold.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyTranslationXAndShadow.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyTranslationXAndShadow.png
new file mode 100644
index 000000000000..fe9456dbd9e9
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyTranslationXAndShadow.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyTranslationYAndShadow.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyTranslationYAndShadow.png
new file mode 100644
index 000000000000..9e641169c319
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/VerifyTranslationYAndShadow.png differ
diff --git a/src/Controls/tests/Xaml.UnitTests/Issues/Maui35564.xaml b/src/Controls/tests/Xaml.UnitTests/Issues/Maui35564.xaml
new file mode 100644
index 000000000000..67fffc892e92
--- /dev/null
+++ b/src/Controls/tests/Xaml.UnitTests/Issues/Maui35564.xaml
@@ -0,0 +1,75 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Controls/tests/Xaml.UnitTests/Issues/Maui35564.xaml.cs b/src/Controls/tests/Xaml.UnitTests/Issues/Maui35564.xaml.cs
new file mode 100644
index 000000000000..eae7e7e7294a
--- /dev/null
+++ b/src/Controls/tests/Xaml.UnitTests/Issues/Maui35564.xaml.cs
@@ -0,0 +1,225 @@
+using System;
+using System.Collections.ObjectModel;
+using System.Windows.Input;
+using Microsoft.Maui.Controls.Internals;
+using Microsoft.Maui.Dispatching;
+using Microsoft.Maui.UnitTests;
+using Xunit;
+
+namespace Microsoft.Maui.Controls.Xaml.UnitTests;
+
+///
+/// Regression test for https://github.com/dotnet/maui/issues/35564
+///
+/// Scenario A (Runtime inflator):
+/// A TapGestureRecognizer inside a CollectionView ItemTemplate binds its Command
+/// to the *page* using Source={RelativeSource AncestorType=...}, while the
+/// DataTemplate has x:DataType="local:Maui35564Item" (the item model type).
+/// When IsXamlCBindingWithSourceCompilationEnabled is true (AOT), BindingExtension
+/// must NOT propagate the DataTemplate's x:DataType to a RelativeSource binding.
+///
+/// Scenario B (SourceGen inflator):
+/// The binding has x:DataType=local:Maui35564 directly on it, alongside
+/// Source={RelativeSource AncestorType=...}. SourceGen must compile this to a
+/// TypedBinding (no reflection) so the binding survives AOT/linker trimming.
+///
+/// Scenario C (Regression guard — RelativeSource Self with inherited x:DataType):
+/// A {RelativeSource Self} binding inside a DataTemplate that has x:DataType=
+/// "local:Maui35564Item". The inherited item type must NOT be applied to Self
+/// bindings — before the fix, Maui35564Item.IsAssignableFrom(Label) = false
+/// would null out the source; after the fix DataType = null for inherited-type
+/// RelativeSource bindings, so Self resolves to the Label correctly.
+///
+public partial class Maui35564 : ContentPage
+{
+ public ObservableCollection Items { get; } = new()
+ {
+ new Maui35564Item { Name = "Item A" },
+ new Maui35564Item { Name = "Item B" },
+ };
+
+ public ICommand ItemTappedCommand { get; } = new Command(_ => { });
+
+ public Maui35564()
+ {
+ InitializeComponent();
+ BindingContext = this;
+ }
+
+ [Collection("Issue")]
+ public class Tests : IDisposable
+ {
+ const string FeatureSwitch =
+ "Microsoft.Maui.RuntimeFeature.IsXamlCBindingWithSourceCompilationEnabled";
+
+ public Tests() => DispatcherProvider.SetCurrent(new DispatcherProviderStub());
+ public void Dispose() => DispatcherProvider.SetCurrent(null);
+
+ ///
+ /// Scenario A: RelativeSource binding without x:DataType directly on the binding node.
+ /// The DataTemplate's inherited x:DataType (Maui35564Item) must NOT be used to validate
+ /// the RelativeSource binding's resolved ancestor (the Maui35564 page).
+ /// For SourceGen, AncestorType should be resolved as the canonical source type and
+ /// compiled to TypedBinding even without x:DataType on the binding node.
+ ///
+ [Theory]
+ [XamlInflatorData]
+ internal void RelativeSourceCommandBindsToAncestorWithXamlCCompilationEnabled(XamlInflator inflator)
+ {
+ AppContext.SetSwitch(FeatureSwitch, true);
+ try
+ {
+ var page = new Maui35564(inflator);
+ page.BindingContext = page;
+
+ var itemLayout = page.TheCollectionView.ItemTemplate.CreateContent() as VerticalStackLayout;
+ Assert.NotNull(itemLayout);
+
+ var container = new VerticalStackLayout();
+ container.Add(itemLayout);
+ page.Content = container;
+
+ itemLayout.BindingContext = new Maui35564Item { Name = "Test" };
+
+ var tapGesture = itemLayout.GestureRecognizers[0] as TapGestureRecognizer;
+ Assert.NotNull(tapGesture);
+
+ Assert.NotNull(tapGesture.Command);
+ Assert.Same(page.ItemTappedCommand, tapGesture.Command);
+
+ if (inflator == XamlInflator.SourceGen)
+ {
+ var binding = tapGesture.GetContext(TapGestureRecognizer.CommandProperty).Bindings.GetValue();
+ Assert.IsAssignableFrom(binding);
+ }
+ }
+ finally
+ {
+ AppContext.SetSwitch(FeatureSwitch, false);
+ }
+ }
+
+ ///
+ /// Scenario B: RelativeSource binding WITH x:DataType directly on the binding node.
+ /// This is the real-world pattern users write in AOT apps. SourceGen must compile it
+ /// to a TypedBinding (no reflection) so the binding survives linker trimming.
+ /// For all inflators, the Command must resolve correctly.
+ /// For the SourceGen inflator specifically, the binding must be a TypedBinding.
+ ///
+ [Theory]
+ [XamlInflatorData]
+ internal void RelativeSourceCommandWithExplicitXDataTypeCompilesTypedBinding(XamlInflator inflator)
+ {
+ AppContext.SetSwitch(FeatureSwitch, true);
+ try
+ {
+ var page = new Maui35564(inflator);
+ page.BindingContext = page;
+
+ var itemLayout = page.TheCollectionView2.ItemTemplate.CreateContent() as VerticalStackLayout;
+ Assert.NotNull(itemLayout);
+
+ var container = new VerticalStackLayout();
+ container.Add(itemLayout);
+ page.Content = container;
+
+ itemLayout.BindingContext = new Maui35564Item { Name = "Test" };
+
+ var tapGesture = itemLayout.GestureRecognizers[0] as TapGestureRecognizer;
+ Assert.NotNull(tapGesture);
+
+ // Command must resolve to the page's command for ALL inflators.
+ Assert.NotNull(tapGesture.Command);
+ Assert.Same(page.ItemTappedCommand, tapGesture.Command);
+
+ // For the SourceGen inflator, the binding must be a TypedBinding — not a reflective
+ // Binding — so it survives AOT linker trimming.
+ if (inflator == XamlInflator.SourceGen)
+ {
+ var binding = tapGesture.GetContext(TapGestureRecognizer.CommandProperty).Bindings.GetValue();
+ Assert.IsAssignableFrom(binding);
+ }
+ }
+ finally
+ {
+ AppContext.SetSwitch(FeatureSwitch, false);
+ }
+ }
+ ///
+ /// Scenario C: {RelativeSource Self} inside a DataTemplate that has x:DataType="Maui35564Item".
+ /// The inherited item type (Maui35564Item) must NOT be applied to the Self binding —
+ /// Self resolves to the Label itself, not to the DataTemplate item.
+ /// Before the fix: inherited DataType caused IsAssignableFrom(Label)=false → source=null.
+ /// After the fix: DataType=null for inherited-type RelativeSource bindings → Self resolves.
+ ///
+ [Theory]
+ [XamlInflatorData]
+ internal void RelativeSourceSelfInsideDataTemplateWithInheritedXDataType(XamlInflator inflator)
+ {
+ AppContext.SetSwitch(FeatureSwitch, true);
+ try
+ {
+ var page = new Maui35564(inflator);
+ page.BindingContext = page;
+
+ var itemLayout = page.TheCollectionView3.ItemTemplate.CreateContent() as VerticalStackLayout;
+ Assert.NotNull(itemLayout);
+
+ itemLayout.BindingContext = new Maui35564Item { Name = "Test" };
+
+ var label = itemLayout.Children[0] as Label;
+ Assert.NotNull(label);
+
+ // Label.Text must equal the Label's own AutomationId ("scenario-c"), bound via {RelativeSource Self}.
+ // If the inherited x:DataType were applied, the Self source would be nulled out
+ // and Text would be null or empty.
+ Assert.Equal("scenario-c", label.Text);
+ }
+ finally
+ {
+ AppContext.SetSwitch(FeatureSwitch, false);
+ }
+ }
+
+ ///
+ /// Scenario D: {RelativeSource Self} with x:DataType directly on the binding node.
+ /// Self bindings should not be compiled to TypedBinding, even with explicit x:DataType,
+ /// because the source is the view element itself.
+ ///
+ [Theory]
+ [XamlInflatorData]
+ internal void RelativeSourceSelfWithExplicitXDataTypeStaysUncompiled(XamlInflator inflator)
+ {
+ AppContext.SetSwitch(FeatureSwitch, true);
+ try
+ {
+ var page = new Maui35564(inflator);
+ page.BindingContext = page;
+
+ var itemLayout = page.TheCollectionView4.ItemTemplate.CreateContent() as VerticalStackLayout;
+ Assert.NotNull(itemLayout);
+
+ itemLayout.BindingContext = new Maui35564Item { Name = "Test" };
+
+ var label = itemLayout.Children[0] as Label;
+ Assert.NotNull(label);
+ Assert.Equal("scenario-d", label.Text);
+
+ if (inflator == XamlInflator.SourceGen)
+ {
+ var binding = label.GetContext(Label.TextProperty).Bindings.GetValue();
+ Assert.IsNotAssignableFrom(binding);
+ }
+ }
+ finally
+ {
+ AppContext.SetSwitch(FeatureSwitch, false);
+ }
+ }
+ }
+}
+
+public class Maui35564Item
+{
+ public string Name { get; set; } = string.Empty;
+}
diff --git a/src/Controls/tests/Xaml.UnitTests/MSBuild/MSBuildTests.cs b/src/Controls/tests/Xaml.UnitTests/MSBuild/MSBuildTests.cs
index 5e6ee85446e7..1b491c1f18e9 100644
--- a/src/Controls/tests/Xaml.UnitTests/MSBuild/MSBuildTests.cs
+++ b/src/Controls/tests/Xaml.UnitTests/MSBuild/MSBuildTests.cs
@@ -186,6 +186,13 @@ XElement AddFile(string name, string buildAction, string contents)
return itemGroup;
}
+ void WriteFile(string name, string contents)
+ {
+ var filePath = IOPath.Combine(tempDirectory, name.Replace('\\', IOPath.DirectorySeparatorChar).Replace('/', IOPath.DirectorySeparatorChar));
+ Directory.CreateDirectory(IOPath.GetDirectoryName(filePath));
+ File.WriteAllText(filePath, contents);
+ }
+
string Build(string projectFile, string target = "Build", string verbosity = "normal", string additionalArgs = "", bool shouldSucceed = true)
{
var builder = new StringBuilder();
@@ -266,6 +273,64 @@ void AssertDoesNotExist(string path)
Assert.False(File.Exists(path), $"{path} should *not* exist!");
}
+ void AssertTypeExists(string assemblyPath, string fullTypeName)
+ {
+ using var assembly = AssemblyDefinition.ReadAssembly(assemblyPath);
+ Assert.Contains(assembly.MainModule.Types.Select(t => t.FullName), t => t == fullTypeName);
+ }
+
+ void AssertTypeDoesNotExist(string assemblyPath, string fullTypeName)
+ {
+ using var assembly = AssemblyDefinition.ReadAssembly(assemblyPath);
+ Assert.DoesNotContain(assembly.MainModule.Types.Select(t => t.FullName), t => t == fullTypeName);
+ }
+
+ void AddSingleProjectBeforeTargetsImport(XElement project)
+ {
+ var beforeTargetsPath = AssemblyInfoTests.GetFilePathFromRoot(IOPath.Combine("src", "Controls", "src", "Build.Tasks", "nuget", "buildTransitive", "netstandard2.0", "Microsoft.Maui.Controls.SingleProject.Before.targets"));
+ project.Add(NewElement("Import").WithAttribute("Project", beforeTargetsPath));
+ }
+
+ void AddSingleProjectTargetsImport(XElement project)
+ {
+ var targetsPath = AssemblyInfoTests.GetFilePathFromRoot(IOPath.Combine("src", "Controls", "src", "Build.Tasks", "nuget", "buildTransitive", "netstandard2.0", "Microsoft.Maui.Controls.SingleProject.targets"));
+ project.Add(NewElement("Import").WithAttribute("Project", targetsPath));
+
+ // Assign the synthetic TargetPlatformIdentifier from a private test-only property
+ // inside a target rather than as a global 'dotnet build' property. Passing a platform
+ // TPI globally makes the SDK attempt workload resolution during evaluation for what is a
+ // plain net10.0 (non-platform) project, which fails on CI agents without that workload
+ // (NETSDK1208 / NETSDK1178) before the SingleProject targets under test ever run. Setting
+ // it here — after SDK evaluation but before the SingleProject compile-filtering targets —
+ // keeps the tests workload-neutral while still exercising the TPI-dependent logic (the
+ // allow-list built by _MauiCollectPlatformSpecificCompileItems determines which files are
+ // compiled, so it does not rely on the evaluation-time per-TPI Compile metadata flip).
+ var applyTpiTarget = NewElement("Target")
+ .WithAttribute("Name", "_ApplyTestTargetPlatformIdentifier")
+ .WithAttribute("BeforeTargets", "_MauiNormalizePlatformSpecificFolders;_MauiCollectPlatformSpecificCompileItems;_MauiRemovePlatformCompileItems")
+ .WithAttribute("Condition", " '$(_SingleProjectTestTargetPlatformIdentifier)' != '' ");
+ var tpiPropertyGroup = NewElement("PropertyGroup");
+ tpiPropertyGroup.Add(NewElement("TargetPlatformIdentifier").WithValue("$(_SingleProjectTestTargetPlatformIdentifier)"));
+ applyTpiTarget.Add(tpiPropertyGroup);
+ project.Add(applyTpiTarget);
+ }
+
+ void AddMauiReferences(XElement project)
+ {
+ var itemGroup = NewElement("ItemGroup");
+ foreach (var assembly in references)
+ {
+ var reference = NewElement("Reference").WithAttribute("Include", assembly);
+ if (assembly.EndsWith(".dll", StringComparison.OrdinalIgnoreCase))
+ {
+ reference.Add(NewElement("HintPath").WithValue(IOPath.Combine("..", "..", assembly)));
+ }
+ itemGroup.Add(reference);
+ }
+
+ project.Add(itemGroup);
+ }
+
[Fact]
public void BuildAProject()
{
@@ -564,6 +629,390 @@ public void NoXamlFiles()
Assert.False(log.Contains("Building target \"XamlC\"", StringComparison.Ordinal), "XamlC should be skipped if there are no .xaml files.");
}
+ [Theory]
+ [InlineData("ios", "ios;maccatalyst", true)]
+ [InlineData("maccatalyst", "ios;maccatalyst", true)]
+ [InlineData("android", "ios;maccatalyst", false)]
+ [InlineData("ios", "ios; maccatalyst", true)]
+ [InlineData("maccatalyst", "ios; maccatalyst", true)]
+ [InlineData("android", "ios; maccatalyst", false)]
+ // Tab and mixed whitespace in the list — see Regex.Replace(\s+, '') in
+ // _MauiCollectPlatformSpecificCompileItems. ASCII-space-only stripping
+ // (.Replace(' ', '')) would silently miss these and break shared folders.
+ [InlineData("ios", "ios;\tmaccatalyst", true)]
+ [InlineData("maccatalyst", "ios;\tmaccatalyst", true)]
+ [InlineData("ios", "ios; \t maccatalyst", true)]
+ [InlineData("maccatalyst", "ios; \t maccatalyst", true)]
+ public void SingleProject_SharedPlatformFolderMappingsAreRespected(string targetPlatformIdentifier, string targetPlatformIdentifiers, bool shouldIncludeAppleSharedFile)
+ {
+ SetUp();
+ var project = NewElement("Project").WithAttribute("Sdk", "Microsoft.NET.Sdk");
+ var propertyGroup = NewElement("PropertyGroup");
+ propertyGroup.Add(NewElement("TargetFramework").WithValue(GetTfm()));
+ propertyGroup.Add(NewElement("SingleProject").WithValue("true"));
+ project.Add(propertyGroup);
+ AddMauiReferences(project);
+ AddSingleProjectBeforeTargetsImport(project);
+
+ var customMappings = NewElement("ItemGroup");
+ customMappings.Add(NewElement("MauiPlatformSpecificFolder")
+ .WithAttribute("Include", "Platforms\\Apple\\")
+ .WithAttribute("TargetPlatformIdentifiers", targetPlatformIdentifiers));
+ project.Add(customMappings);
+
+ WriteFile("Entry.cs", @"
+namespace Microsoft.Maui.Controls.Xaml.UnitTests;
+
+public static partial class CurrentPlatform
+{
+}
+
+public static class Entry
+{
+ public static string Value => CurrentPlatform.Name;
+}");
+
+ WriteFile("Platforms\\iOS\\CurrentPlatform.cs", @"
+namespace Microsoft.Maui.Controls.Xaml.UnitTests;
+
+public static partial class CurrentPlatform
+{
+ public static string Name => ""iOS"";
+}");
+
+ WriteFile("Platforms\\MacCatalyst\\CurrentPlatform.cs", @"
+namespace Microsoft.Maui.Controls.Xaml.UnitTests;
+
+public static partial class CurrentPlatform
+{
+ public static string Name => ""MacCatalyst"";
+}");
+
+ WriteFile("Platforms\\Android\\CurrentPlatform.cs", @"
+namespace Microsoft.Maui.Controls.Xaml.UnitTests;
+
+public static partial class CurrentPlatform
+{
+ public static string Name => ""Android"";
+}");
+
+ WriteFile("Platforms\\Apple\\AppleSharedMarker.cs", @"
+namespace Microsoft.Maui.Controls.Xaml.UnitTests;
+
+public static class AppleSharedMarker
+{
+ public static string Value => ""Apple"";
+}");
+
+ AddSingleProjectTargetsImport(project);
+
+ var projectFile = IOPath.Combine(tempDirectory, "test.csproj");
+ project.Save(projectFile);
+
+ Build(projectFile, additionalArgs: $"-p:_SingleProjectTestTargetPlatformIdentifier={targetPlatformIdentifier}");
+
+ var testDll = IOPath.Combine(intermediateDirectory, "test.dll");
+ AssertExists(testDll, nonEmpty: true);
+
+ if (shouldIncludeAppleSharedFile)
+ AssertTypeExists(testDll, "Microsoft.Maui.Controls.Xaml.UnitTests.AppleSharedMarker");
+ else
+ AssertTypeDoesNotExist(testDll, "Microsoft.Maui.Controls.Xaml.UnitTests.AppleSharedMarker");
+ }
+
+ [Fact]
+ public void SingleProject_BuiltInPlatformFoldersCanBeExtended()
+ {
+ SetUp();
+ var project = NewElement("Project").WithAttribute("Sdk", "Microsoft.NET.Sdk");
+ var propertyGroup = NewElement("PropertyGroup");
+ propertyGroup.Add(NewElement("TargetFramework").WithValue(GetTfm()));
+ propertyGroup.Add(NewElement("SingleProject").WithValue("true"));
+ project.Add(propertyGroup);
+ AddMauiReferences(project);
+ AddSingleProjectBeforeTargetsImport(project);
+
+ // Real users author this as a Target rather than an inline ItemGroup.
+ // In a normal SDK-style csproj, the user's project body is evaluated
+ // before NuGet imports the SingleProject SDK targets, so an inline
+ // would
+ // silently no-op: $(iOSProjectFolder) is empty at evaluation time and
+ // the built-in items don't exist yet. Hooking _MauiNormalizePlatformSpecificFolders
+ // guarantees the Update runs after the SDK has populated both the
+ // built-in items and $(iOSProjectFolder).
+ var extendTarget = NewElement("Target")
+ .WithAttribute("Name", "MauiExtendIosToMacCatalyst")
+ .WithAttribute("BeforeTargets", "_MauiNormalizePlatformSpecificFolders");
+ var extendItemGroup = NewElement("ItemGroup");
+ var update = NewElement("MauiPlatformSpecificFolder")
+ .WithAttribute("Update", "$(iOSProjectFolder)");
+ update.Add(NewElement("TargetPlatformIdentifiers").WithValue("ios;maccatalyst"));
+ extendItemGroup.Add(update);
+ extendTarget.Add(extendItemGroup);
+ project.Add(extendTarget);
+
+ WriteFile("Entry.cs", @"
+namespace Microsoft.Maui.Controls.Xaml.UnitTests;
+
+public static class Entry
+{
+ public static string Value => ExtendedIosMarker.Value;
+}");
+
+ WriteFile("Platforms\\iOS\\ExtendedIosMarker.cs", @"
+namespace Microsoft.Maui.Controls.Xaml.UnitTests;
+
+public static class ExtendedIosMarker
+{
+ public static string Value => ""SharedWithCatalyst"";
+}");
+
+ AddSingleProjectTargetsImport(project);
+
+ var projectFile = IOPath.Combine(tempDirectory, "test.csproj");
+ project.Save(projectFile);
+
+ Build(projectFile, additionalArgs: "-p:_SingleProjectTestTargetPlatformIdentifier=maccatalyst");
+
+ var testDll = IOPath.Combine(intermediateDirectory, "test.dll");
+ AssertExists(testDll, nonEmpty: true);
+ AssertTypeExists(testDll, "Microsoft.Maui.Controls.Xaml.UnitTests.ExtendedIosMarker");
+ }
+
+ // Regression test: a user-supplied folder declared without a trailing slash
+ // must NOT match sibling folders sharing a common prefix. Without
+ // EnsureTrailingSlash() in _MauiCollectPlatformSpecificCompileItems, the
+ // glob "Platforms\Apple**/*.cs" would silently include AppleX/AppleLegacy.
+ [Theory]
+ [InlineData("Platforms\\Apple", "ios")]
+ [InlineData("Platforms\\Apple\\", "ios")]
+ [InlineData("Platforms\\Apple", "maccatalyst")]
+ [InlineData("Platforms\\Apple\\", "maccatalyst")]
+ public void SingleProject_PlatformFolderWithoutTrailingSlashDoesNotMatchSiblingFolders(string includePath, string targetPlatformIdentifier)
+ {
+ SetUp();
+ var project = NewElement("Project").WithAttribute("Sdk", "Microsoft.NET.Sdk");
+ var propertyGroup = NewElement("PropertyGroup");
+ propertyGroup.Add(NewElement("TargetFramework").WithValue(GetTfm()));
+ propertyGroup.Add(NewElement("SingleProject").WithValue("true"));
+ project.Add(propertyGroup);
+ AddMauiReferences(project);
+ AddSingleProjectBeforeTargetsImport(project);
+
+ var customMappings = NewElement("ItemGroup");
+ customMappings.Add(NewElement("MauiPlatformSpecificFolder")
+ .WithAttribute("Include", includePath)
+ .WithAttribute("TargetPlatformIdentifiers", "ios;maccatalyst"));
+ project.Add(customMappings);
+
+ WriteFile("Entry.cs", @"
+namespace Microsoft.Maui.Controls.Xaml.UnitTests;
+
+public static class Entry
+{
+ public static string Value => ""ok"";
+}");
+
+ WriteFile("Platforms\\Apple\\AppleSharedMarker.cs", @"
+namespace Microsoft.Maui.Controls.Xaml.UnitTests;
+
+public static class AppleSharedMarker
+{
+ public static string Value => ""Apple"";
+}");
+
+ // Sibling folder with a common prefix — must NOT be picked up by the
+ // "Apple" mapping regardless of trailing-slash authoring.
+ WriteFile("Platforms\\AppleX\\AppleXMarker.cs", @"
+namespace Microsoft.Maui.Controls.Xaml.UnitTests;
+
+public static class AppleXMarker
+{
+ public static string Value => ""AppleX"";
+}");
+
+ AddSingleProjectTargetsImport(project);
+
+ var projectFile = IOPath.Combine(tempDirectory, "test.csproj");
+ project.Save(projectFile);
+
+ Build(projectFile, additionalArgs: $"-p:_SingleProjectTestTargetPlatformIdentifier={targetPlatformIdentifier}");
+
+ var testDll = IOPath.Combine(intermediateDirectory, "test.dll");
+ AssertExists(testDll, nonEmpty: true);
+ AssertTypeExists(testDll, "Microsoft.Maui.Controls.Xaml.UnitTests.AppleSharedMarker");
+ AssertTypeDoesNotExist(testDll, "Microsoft.Maui.Controls.Xaml.UnitTests.AppleXMarker");
+ }
+
+ // Non-platform builds (TargetPlatformIdentifier empty, e.g. design-time
+ // or netstandard TFM) must keep removing platform folders that declare a
+ // non-empty TargetPlatformIdentifiers. Condition-gated folders with empty
+ // TargetPlatformIdentifiers must still participate when their condition
+ // evaluates to true — locks in the "empty TPI = always include" branch.
+ [Fact]
+ public void SingleProject_NonPlatformBuildExcludesPlatformSpecificFoldersButKeepsConditionGated()
+ {
+ SetUp();
+ var project = NewElement("Project").WithAttribute("Sdk", "Microsoft.NET.Sdk");
+ var propertyGroup = NewElement("PropertyGroup");
+ propertyGroup.Add(NewElement("TargetFramework").WithValue(GetTfm()));
+ propertyGroup.Add(NewElement("SingleProject").WithValue("true"));
+ project.Add(propertyGroup);
+ AddMauiReferences(project);
+ AddSingleProjectBeforeTargetsImport(project);
+
+ var customMappings = NewElement("ItemGroup");
+ customMappings.Add(NewElement("MauiPlatformSpecificFolder")
+ .WithAttribute("Include", "Platforms\\Apple\\")
+ .WithAttribute("TargetPlatformIdentifiers", "ios;maccatalyst"));
+ customMappings.Add(NewElement("MauiPlatformSpecificFolder")
+ .WithAttribute("Include", "Platforms\\Shared\\"));
+ project.Add(customMappings);
+
+ WriteFile("Entry.cs", @"
+namespace Microsoft.Maui.Controls.Xaml.UnitTests;
+
+public static class Entry
+{
+ public static string Value => ""ok"";
+}");
+
+ WriteFile("Platforms\\Apple\\AppleSharedMarker.cs", @"
+namespace Microsoft.Maui.Controls.Xaml.UnitTests;
+
+public static class AppleSharedMarker
+{
+ public static string Value => ""Apple"";
+}");
+
+ WriteFile("Platforms\\Shared\\SharedMarker.cs", @"
+namespace Microsoft.Maui.Controls.Xaml.UnitTests;
+
+public static class SharedMarker
+{
+ public static string Value => ""Shared"";
+}");
+
+ AddSingleProjectTargetsImport(project);
+
+ var projectFile = IOPath.Combine(tempDirectory, "test.csproj");
+ project.Save(projectFile);
+
+ // No -p:TargetPlatformIdentifier — simulates the non-platform TFM /
+ // design-time evaluation scenario.
+ Build(projectFile);
+
+ var testDll = IOPath.Combine(intermediateDirectory, "test.dll");
+ AssertExists(testDll, nonEmpty: true);
+ AssertTypeDoesNotExist(testDll, "Microsoft.Maui.Controls.Xaml.UnitTests.AppleSharedMarker");
+ AssertTypeExists(testDll, "Microsoft.Maui.Controls.Xaml.UnitTests.SharedMarker");
+ }
+
+ [Theory]
+ [InlineData("ios", true)]
+ [InlineData("maccatalyst", false)]
+ [InlineData("android", false)]
+ public void SingleProject_SingularPlatformFolderMetadataRemainsBackwardCompatible(string targetPlatformIdentifier, bool shouldIncludeLegacyFile)
+ {
+ SetUp();
+ var project = NewElement("Project").WithAttribute("Sdk", "Microsoft.NET.Sdk");
+ var propertyGroup = NewElement("PropertyGroup");
+ propertyGroup.Add(NewElement("TargetFramework").WithValue(GetTfm()));
+ propertyGroup.Add(NewElement("SingleProject").WithValue("true"));
+ project.Add(propertyGroup);
+ AddMauiReferences(project);
+ AddSingleProjectBeforeTargetsImport(project);
+
+ var customMappings = NewElement("ItemGroup");
+ customMappings.Add(NewElement("MauiPlatformSpecificFolder")
+ .WithAttribute("Include", "Platforms\\LegacyiOS\\")
+ .WithAttribute("TargetPlatformIdentifier", "ios"));
+ project.Add(customMappings);
+
+ WriteFile("Entry.cs", @"
+namespace Microsoft.Maui.Controls.Xaml.UnitTests;
+
+public static class Entry
+{
+ public static string Value => ""ok"";
+}");
+
+ WriteFile("Platforms\\LegacyiOS\\LegacyIosMarker.cs", @"
+namespace Microsoft.Maui.Controls.Xaml.UnitTests;
+
+public static class LegacyIosMarker
+{
+ public static string Value => ""LegacyiOS"";
+}");
+
+ AddSingleProjectTargetsImport(project);
+
+ var projectFile = IOPath.Combine(tempDirectory, "test.csproj");
+ project.Save(projectFile);
+
+ Build(projectFile, additionalArgs: $"-p:_SingleProjectTestTargetPlatformIdentifier={targetPlatformIdentifier}");
+
+ var testDll = IOPath.Combine(intermediateDirectory, "test.dll");
+ AssertExists(testDll, nonEmpty: true);
+
+ if (shouldIncludeLegacyFile)
+ AssertTypeExists(testDll, "Microsoft.Maui.Controls.Xaml.UnitTests.LegacyIosMarker");
+ else
+ AssertTypeDoesNotExist(testDll, "Microsoft.Maui.Controls.Xaml.UnitTests.LegacyIosMarker");
+ }
+
+ [Theory]
+ [InlineData(false)]
+ [InlineData(true)]
+ public void SingleProject_ConditionGatedPlatformFoldersCanParticipateWithoutATargetPlatformIdentifier(bool useLinuxFolder)
+ {
+ SetUp();
+ var project = NewElement("Project").WithAttribute("Sdk", "Microsoft.NET.Sdk");
+ var propertyGroup = NewElement("PropertyGroup");
+ propertyGroup.Add(NewElement("TargetFramework").WithValue(GetTfm()));
+ propertyGroup.Add(NewElement("SingleProject").WithValue("true"));
+ project.Add(propertyGroup);
+ AddMauiReferences(project);
+ AddSingleProjectBeforeTargetsImport(project);
+
+ var customMappings = NewElement("ItemGroup");
+ customMappings.Add(NewElement("MauiPlatformSpecificFolder")
+ .WithAttribute("Include", "Platforms\\Linux\\")
+ .WithAttribute("Condition", " '$(UseLinuxFolder)' == 'true' "));
+ project.Add(customMappings);
+
+ WriteFile("Entry.cs", @"
+namespace Microsoft.Maui.Controls.Xaml.UnitTests;
+
+public static class Entry
+{
+ public static string Value => ""ok"";
+}");
+
+ WriteFile("Platforms\\Linux\\LinuxMarker.cs", @"
+namespace Microsoft.Maui.Controls.Xaml.UnitTests;
+
+public static class LinuxMarker
+{
+ public static string Value => ""Linux"";
+}");
+
+ AddSingleProjectTargetsImport(project);
+
+ var projectFile = IOPath.Combine(tempDirectory, "test.csproj");
+ project.Save(projectFile);
+
+ Build(projectFile, additionalArgs: $"-p:UseLinuxFolder={useLinuxFolder.ToString().ToLowerInvariant()}");
+
+ var testDll = IOPath.Combine(intermediateDirectory, "test.dll");
+ AssertExists(testDll, nonEmpty: true);
+
+ if (useLinuxFolder)
+ AssertTypeExists(testDll, "Microsoft.Maui.Controls.Xaml.UnitTests.LinuxMarker");
+ else
+ AssertTypeDoesNotExist(testDll, "Microsoft.Maui.Controls.Xaml.UnitTests.LinuxMarker");
+ }
+
///
/// Tests that the SingleProject Before targets respect custom CodesignEntitlements properties
///
diff --git a/src/Core/AndroidNative/maui/src/main/java/com/microsoft/maui/PlatformDrawable.java b/src/Core/AndroidNative/maui/src/main/java/com/microsoft/maui/PlatformDrawable.java
index 9a32c1aac9a9..62fa0cc2e36f 100644
--- a/src/Core/AndroidNative/maui/src/main/java/com/microsoft/maui/PlatformDrawable.java
+++ b/src/Core/AndroidNative/maui/src/main/java/com/microsoft/maui/PlatformDrawable.java
@@ -252,7 +252,14 @@ private void tryUpdateClipPath() {
// PlatformShadowDrawable implementation
@Override
public boolean canDrawShadow() {
- return this.backgroundStyle.getIsSolid() && (this.strokeThickness == 0 || this.borderStyle.getPaintType() == PlatformPaintType.NONE || this.borderStyle.getIsSolid());
+ // Fast-path is safe when the background defines the silhouette and the stroke
+ // contributes no visible pixels (thickness=0, no paint, opaque, or transparent).
+ // See #36942 — Border { Stroke=Transparent } falls here to avoid the SW-bake path.
+ return this.backgroundStyle.getIsSolid()
+ && (this.strokeThickness == 0
+ || this.borderStyle.getPaintType() == PlatformPaintType.NONE
+ || this.borderStyle.getIsSolid()
+ || this.borderStyle.getIsFullyTransparent());
}
@Override
@@ -268,7 +275,11 @@ public void drawShadow(Canvas canvas, Paint shadowPaint, Path outerClipPath) {
if (this.fullClipPath == null) {
return;
}
- contentPath = this.fullClipPath;
+ // Use the inner clipPath when the border draws no pixels so the shadow
+ // hugs the visible fill instead of the outer stroke bounds (#36942).
+ contentPath = this.borderStyle.getIsFullyTransparent()
+ ? this.clipPath
+ : this.fullClipPath;
} else {
contentPath = new Path();
contentPath.addRect(0, 0, this.width, this.height, Path.Direction.CW);
diff --git a/src/Core/AndroidNative/maui/src/main/java/com/microsoft/maui/PlatformDrawableStyle.java b/src/Core/AndroidNative/maui/src/main/java/com/microsoft/maui/PlatformDrawableStyle.java
index 7e0d03d7ba64..a3a1bf9d502e 100644
--- a/src/Core/AndroidNative/maui/src/main/java/com/microsoft/maui/PlatformDrawableStyle.java
+++ b/src/Core/AndroidNative/maui/src/main/java/com/microsoft/maui/PlatformDrawableStyle.java
@@ -51,6 +51,25 @@ private boolean computeIsSolid() {
return Color.alpha(this.solidColor) == 255;
}
+ // Returns true when this paint contributes no visible pixels — used by canDrawShadow
+ // to treat a fully-transparent border as equivalent to no border for shadow silhouette.
+ public boolean getIsFullyTransparent() {
+ if (this.paintType == PlatformPaintType.NONE) {
+ return true;
+ }
+
+ if (this.gradientColors != null) {
+ for (int i = 0; i < this.gradientColors.length; i++) {
+ if (Color.alpha(this.gradientColors[i]) != 0) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ return Color.alpha(this.solidColor) == 0;
+ }
+
public int getPaintType() {
return this.paintType;
}
@@ -60,6 +79,10 @@ private Shader getShader(int width, int height) {
return null;
}
+ if (width == 0 && height == 0) {
+ return null;
+ }
+
if (width != this.shaderWidth || height != this.shaderHeight) {
this.shaderWidth = width;
this.shaderHeight = height;
diff --git a/src/Core/maps/src/Handlers/Map/MapHandler.Android.cs b/src/Core/maps/src/Handlers/Map/MapHandler.Android.cs
index 2426f2a00557..696ba7c46b1c 100644
--- a/src/Core/maps/src/Handlers/Map/MapHandler.Android.cs
+++ b/src/Core/maps/src/Handlers/Map/MapHandler.Android.cs
@@ -37,6 +37,7 @@ public partial class MapHandler : ViewHandler
List? _polygons;
List? _circles;
Dictionary? _trackedMapElements;
+ HashSet? _subscribedPins;
public GoogleMap? Map { get; private set; }
@@ -503,7 +504,8 @@ void AddPins(IList pins)
_markers.Add(marker!);
}
- if (pin is INotifyPropertyChanged observable)
+ _subscribedPins ??= new HashSet(ReferenceEqualityComparer.Instance);
+ if (_subscribedPins.Add(pin) && pin is INotifyPropertyChanged observable)
{
observable.PropertyChanged += PinOnPropertyChanged;
}
@@ -556,17 +558,18 @@ void PinOnPropertyChanged(object? sender, PropertyChangedEventArgs e)
void DisconnectPins()
{
- if (VirtualView == null)
- return;
-
- for (int i = 0; i < VirtualView.Pins.Count; i++)
+ if (_subscribedPins is not null)
{
- var pin = VirtualView.Pins[i];
- if (pin is INotifyPropertyChanged observable)
+ foreach (var pin in _subscribedPins)
{
- observable.PropertyChanged -= PinOnPropertyChanged;
+ if (pin is INotifyPropertyChanged observable)
+ {
+ observable.PropertyChanged -= PinOnPropertyChanged;
+ }
+
+ pin?.Handler?.DisconnectHandler();
}
- pin?.Handler?.DisconnectHandler();
+ _subscribedPins = null;
}
}
diff --git a/src/Core/src/Animations/PlatformTicker.Windows.cs b/src/Core/src/Animations/PlatformTicker.Windows.cs
index 35dc7ac8e9d0..92625ca13f66 100644
--- a/src/Core/src/Animations/PlatformTicker.Windows.cs
+++ b/src/Core/src/Animations/PlatformTicker.Windows.cs
@@ -5,15 +5,32 @@ namespace Microsoft.Maui.Animations
///
public class PlatformTicker : Ticker
{
+ bool _isRunning;
+
+ ///
+ public override bool IsRunning => _isRunning;
+
///
public override void Start()
{
+ if (_isRunning)
+ {
+ return;
+ }
+
+ _isRunning = true;
CompositionTarget.Rendering += RenderingFrameEventHandler;
}
///
public override void Stop()
{
+ if (!_isRunning)
+ {
+ return;
+ }
+
+ _isRunning = false;
CompositionTarget.Rendering -= RenderingFrameEventHandler;
}
diff --git a/src/Core/src/Core/ISwipeView.cs b/src/Core/src/Core/ISwipeView.cs
index 8c27bce53a98..1a0e111810d6 100644
--- a/src/Core/src/Core/ISwipeView.cs
+++ b/src/Core/src/Core/ISwipeView.cs
@@ -6,7 +6,7 @@
public interface ISwipeView : IContentView
{
///
- /// Gets a value that represents the minimum swipe distance that must be achieved for a swipe to be recognized.
+ /// Gets a value that represents the swipe distance that must be achieved for a swipe to be recognized.
///
public double Threshold { get; }
diff --git a/src/Core/src/Fonts/FontManager.iOS.cs b/src/Core/src/Fonts/FontManager.iOS.cs
index 0bfd45a3e9f2..4bde070407b4 100644
--- a/src/Core/src/Fonts/FontManager.iOS.cs
+++ b/src/Core/src/Fonts/FontManager.iOS.cs
@@ -1,12 +1,13 @@
using System;
using System.Collections.Concurrent;
+using Foundation;
using Microsoft.Extensions.Logging;
using UIKit;
namespace Microsoft.Maui
{
///
- public class FontManager : IFontManager
+ public class FontManager : IFontManager, IDisposable
{
// UIFontWeight[Constant] is internal in Xamarin.iOS but the convertion from
// the public (int-based) enum is not helpful in this case.
@@ -28,6 +29,7 @@ public class FontManager : IFontManager
readonly IFontRegistrar _fontRegistrar;
readonly IServiceProvider? _serviceProvider;
+ NSObject? _contentSizeCategoryObserver;
UIFont? _defaultFont;
///
@@ -40,6 +42,11 @@ public FontManager(IFontRegistrar fontRegistrar, IServiceProvider? serviceProvid
{
_fontRegistrar = fontRegistrar;
_serviceProvider = serviceProvider;
+
+ // When the preferred content size category changes (Dynamic Type),
+ // clear the font cache so subsequent requests create new fonts
+ // with the current content size category scaling.
+ _contentSizeCategoryObserver = UIApplication.Notifications.ObserveContentSizeCategoryChanged((sender, args) => _fonts.Clear());
}
///
@@ -183,6 +190,12 @@ UIFont ApplyScaling(Font font, UIFont uiFont)
}
}
+ public void Dispose()
+ {
+ _contentSizeCategoryObserver?.Dispose();
+ _contentSizeCategoryObserver = null;
+ }
+
string? CleanseFontName(string fontName)
{
// First check Alias
diff --git a/src/Core/src/Handlers/HybridWebView/HybridWebView.js b/src/Core/src/Handlers/HybridWebView/HybridWebView.js
index a4acb769e63d..23a12054f1fc 100644
--- a/src/Core/src/Handlers/HybridWebView/HybridWebView.js
+++ b/src/Core/src/Handlers/HybridWebView/HybridWebView.js
@@ -6,6 +6,9 @@
* directly. To make changes, modify the TypeScript file and then recompile it.
*/
(() => {
+ // Must stay in sync with HybridWebViewHandler.InvokeDotNetPath / SendMessagePath.
+ const InvokeDotNetEndpoint = '__hwvInvokeDotNet';
+ const SendMessageEndpoint = '__hwvSendMessage';
// Cached function to send messages to the host application.
let sendMessageFunction = null;
/*
@@ -20,8 +23,10 @@
// Determine the mechanism to receive messages from the host application.
if (window.chrome && window.chrome.webview && window.chrome.webview.addEventListener) {
// Windows WebView2
+ // The .NET side URL-encodes messages (see MauiHybridWebView.SendRawMessage) so embedded
+ // NUL characters survive WebView2's null-terminated string marshalling. Decode here.
window.chrome.webview.addEventListener('message', (arg) => {
- dispatchHybridWebViewMessage(arg.data);
+ dispatchHybridWebViewMessage(decodeURIComponent(arg.data));
});
}
else if (window.webkit && window.webkit.messageHandlers && window.webkit.messageHandlers.webwindowinterop) {
@@ -53,15 +58,34 @@
// Determine the function to use to send messages to the host application.
if (window.chrome && window.chrome.webview) {
// Windows WebView2
- sendMessageFunction = msg => window.chrome.webview.postMessage(msg);
+ // URL-encode so embedded NUL characters survive WebView2's null-terminated string
+ // marshalling (TryGetWebMessageAsString returns an LPWSTR); the .NET side decodes it
+ // in HybridWebViewHandler.OnWebMessageReceived.
+ sendMessageFunction = msg => window.chrome.webview.postMessage(encodeURIComponent(msg));
}
else if (window.webkit && window.webkit.messageHandlers && window.webkit.messageHandlers.webwindowinterop) {
// iOS and MacCatalyst WKWebView
sendMessageFunction = msg => window.webkit.messageHandlers.webwindowinterop.postMessage(msg);
}
- else if (window.hybridWebViewHost) {
- // Android WebView
- sendMessageFunction = msg => window.hybridWebViewHost.sendMessage(msg);
+ else {
+ // Android WebView. Sends are chained through a single promise to preserve
+ // FIFO ordering that callers had with the previous synchronous bridge.
+ let sendQueue;
+ sendMessageFunction = msg => {
+ const url = `${window.location.origin}/${SendMessageEndpoint}`;
+ const doSend = () => fetch(url, {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'text/plain',
+ 'X-Maui-Invoke-Token': 'HybridWebView',
+ 'X-Maui-Request-Body': msg
+ },
+ body: msg
+ }).catch(err => {
+ console.error('HybridWebView: failed to send message to .NET host.', err);
+ });
+ sendQueue = sendQueue ? sendQueue.then(doSend) : doSend();
+ };
}
}
/*
@@ -126,7 +150,10 @@
* @param message The message to send to the .NET host application.
*/
function sendRawMessage(message) {
- sendMessageToDotNet('__RawMessage', message);
+ // URL-encode the payload so it survives transports that restrict the byte set
+ // (the Android fetch X-Maui-Request-Body header rejects CR/LF/NUL). Decoded
+ // on the .NET side in HybridWebViewHandler.MessageReceived.
+ sendMessageToDotNet('__RawMessage', encodeURIComponent(message));
}
/*
* Invoke a .NET method on the InvokeJavaScriptTarget instance.
@@ -155,7 +182,7 @@
}
const message = JSON.stringify(body);
// send the request to .NET
- const requestUrl = `${window.location.origin}/__hwvInvokeDotNet`;
+ const requestUrl = `${window.location.origin}/${InvokeDotNetEndpoint}`;
const rawResponse = await fetch(requestUrl, {
method: 'POST',
headers: {
diff --git a/src/Core/src/Handlers/HybridWebView/HybridWebView.ts b/src/Core/src/Handlers/HybridWebView/HybridWebView.ts
index 0ed4a49bd8a7..46e64f11f058 100644
--- a/src/Core/src/Handlers/HybridWebView/HybridWebView.ts
+++ b/src/Core/src/Handlers/HybridWebView/HybridWebView.ts
@@ -27,13 +27,12 @@ interface Window {
};
};
};
-
- // Declare the global object that we have added on Android.
- hybridWebViewHost?: {
- sendMessage: (message: string) => void;
- };
}
+// Must stay in sync with HybridWebViewHandler.InvokeDotNetPath / SendMessagePath.
+const InvokeDotNetEndpoint = '__hwvInvokeDotNet';
+const SendMessageEndpoint = '__hwvSendMessage';
+
/*
* The following interfaces define the shape of the messages that are sent between
* the web view and the .NET host application.
@@ -74,8 +73,10 @@ interface DotNetInvokeResult {
// Determine the mechanism to receive messages from the host application.
if (window.chrome && window.chrome.webview && window.chrome.webview.addEventListener) {
// Windows WebView2
+ // The .NET side URL-encodes messages (see MauiHybridWebView.SendRawMessage) so embedded
+ // NUL characters survive WebView2's null-terminated string marshalling. Decode here.
window.chrome.webview.addEventListener('message', (arg: any) => {
- dispatchHybridWebViewMessage(arg.data);
+ dispatchHybridWebViewMessage(decodeURIComponent(arg.data));
});
} else if (window.webkit && window.webkit.messageHandlers && window.webkit.messageHandlers.webwindowinterop) {
// iOS and MacCatalyst WKWebView
@@ -106,13 +107,32 @@ interface DotNetInvokeResult {
// Determine the function to use to send messages to the host application.
if (window.chrome && window.chrome.webview) {
// Windows WebView2
- sendMessageFunction = msg => window.chrome.webview.postMessage(msg);
+ // URL-encode so embedded NUL characters survive WebView2's null-terminated string
+ // marshalling (TryGetWebMessageAsString returns an LPWSTR); the .NET side decodes it
+ // in HybridWebViewHandler.OnWebMessageReceived.
+ sendMessageFunction = msg => window.chrome.webview.postMessage(encodeURIComponent(msg));
} else if (window.webkit && window.webkit.messageHandlers && window.webkit.messageHandlers.webwindowinterop) {
// iOS and MacCatalyst WKWebView
sendMessageFunction = msg => window.webkit.messageHandlers.webwindowinterop.postMessage(msg);
- } else if (window.hybridWebViewHost) {
- // Android WebView
- sendMessageFunction = msg => window.hybridWebViewHost.sendMessage(msg);
+ } else {
+ // Android WebView. Sends are chained through a single promise to preserve
+ // FIFO ordering that callers had with the previous synchronous bridge.
+ let sendQueue: Promise | undefined;
+ sendMessageFunction = msg => {
+ const url = `${window.location.origin}/${SendMessageEndpoint}`;
+ const doSend = () => fetch(url, {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'text/plain',
+ 'X-Maui-Invoke-Token': 'HybridWebView',
+ 'X-Maui-Request-Body': msg
+ },
+ body: msg
+ }).catch(err => {
+ console.error('HybridWebView: failed to send message to .NET host.', err);
+ });
+ sendQueue = sendQueue ? sendQueue.then(doSend) : doSend();
+ };
}
}
@@ -182,7 +202,10 @@ interface DotNetInvokeResult {
* @param message The message to send to the .NET host application.
*/
function sendRawMessage(message: string) {
- sendMessageToDotNet('__RawMessage', message);
+ // URL-encode the payload so it survives transports that restrict the byte set
+ // (the Android fetch X-Maui-Request-Body header rejects CR/LF/NUL). Decoded
+ // on the .NET side in HybridWebViewHandler.MessageReceived.
+ sendMessageToDotNet('__RawMessage', encodeURIComponent(message));
}
/*
@@ -217,7 +240,7 @@ interface DotNetInvokeResult {
const message = JSON.stringify(body);
// send the request to .NET
- const requestUrl = `${window.location.origin}/__hwvInvokeDotNet`;
+ const requestUrl = `${window.location.origin}/${InvokeDotNetEndpoint}`;
const rawResponse = await fetch(requestUrl, {
method: 'POST',
headers: {
diff --git a/src/Core/src/Handlers/HybridWebView/HybridWebViewHandler.Android.cs b/src/Core/src/Handlers/HybridWebView/HybridWebViewHandler.Android.cs
index 30cac357aecd..1116dd0c5e46 100644
--- a/src/Core/src/Handlers/HybridWebView/HybridWebViewHandler.Android.cs
+++ b/src/Core/src/Handlers/HybridWebView/HybridWebViewHandler.Android.cs
@@ -1,6 +1,5 @@
using System;
using Android.Webkit;
-using Java.Interop;
using static Android.Views.ViewGroup;
using AWebView = Android.Webkit.WebView;
@@ -8,11 +7,6 @@ namespace Microsoft.Maui.Handlers
{
public partial class HybridWebViewHandler : ViewHandler
{
- // This name matches the name of the API used in HybridWebView.js and must remain in sync
- private const string HybridWebViewHostJsName = "hybridWebViewHost";
-
- private HybridWebViewJavaScriptInterface? _javaScriptInterface;
-
protected override AWebView CreatePlatformView()
{
var platformView = new MauiHybridWebView(this, Context!)
@@ -32,8 +26,8 @@ protected override AWebView CreatePlatformView()
platformView.Settings.JavaScriptEnabled = true;
- _javaScriptInterface = new HybridWebViewJavaScriptInterface(this);
- platformView.AddJavascriptInterface(_javaScriptInterface, HybridWebViewHostJsName);
+ // JS -> .NET messages flow through the SendMessagePath HTTP endpoint in
+ // MauiHybridWebViewClient (gated by HasExpectedHeaders), not AddJavascriptInterface.
// Invoke the WebViewInitializing event to allow custom configuration of the web view
var initializingArgs = new WebViewInitializationStartedEventArgs(platformView.Settings);
@@ -46,24 +40,6 @@ protected override AWebView CreatePlatformView()
return platformView;
}
- private sealed class HybridWebViewJavaScriptInterface : HybridJavaScriptInterface
- {
- private readonly WeakReference _hybridWebViewHandler;
-
- public HybridWebViewJavaScriptInterface(HybridWebViewHandler hybridWebViewHandler)
- {
- _hybridWebViewHandler = new(hybridWebViewHandler);
- }
-
- private HybridWebViewHandler? Handler => _hybridWebViewHandler is not null && _hybridWebViewHandler.TryGetTarget(out var h) ? h : null;
-
- [JavascriptInterface]
- public override void SendMessage(string message)
- {
- Handler?.MessageReceived(message);
- }
- }
-
protected override void ConnectHandler(AWebView platformView)
{
base.ConnectHandler(platformView);
diff --git a/src/Core/src/Handlers/HybridWebView/HybridWebViewHandler.Windows.cs b/src/Core/src/Handlers/HybridWebView/HybridWebViewHandler.Windows.cs
index f9cef27e49cd..d40a58668b87 100644
--- a/src/Core/src/Handlers/HybridWebView/HybridWebViewHandler.Windows.cs
+++ b/src/Core/src/Handlers/HybridWebView/HybridWebViewHandler.Windows.cs
@@ -98,7 +98,10 @@ public static void MapSendRawMessage(IHybridWebViewHandler handler, IHybridWebVi
private void OnWebMessageReceived(WebView2 sender, CoreWebView2WebMessageReceivedEventArgs args)
{
- MessageReceived(args.TryGetWebMessageAsString());
+ // The JS transport URL-encodes messages so embedded NUL characters survive WebView2's
+ // null-terminated string marshalling (TryGetWebMessageAsString returns an LPWSTR). Decode
+ // the payload before dispatching it.
+ MessageReceived(Uri.UnescapeDataString(args.TryGetWebMessageAsString()));
}
internal static void MapFlowDirection(IHybridWebViewHandler handler, IHybridWebView hybridWebView)
diff --git a/src/Core/src/Handlers/HybridWebView/HybridWebViewHandler.cs b/src/Core/src/Handlers/HybridWebView/HybridWebViewHandler.cs
index 652164ccf494..77503c435f19 100644
--- a/src/Core/src/Handlers/HybridWebView/HybridWebViewHandler.cs
+++ b/src/Core/src/Handlers/HybridWebView/HybridWebViewHandler.cs
@@ -74,6 +74,7 @@ public partial class HybridWebViewHandler : IHybridWebViewHandler
internal static readonly Uri AppOriginUri = new(AppOrigin);
internal const string InvokeDotNetPath = "__hwvInvokeDotNet";
+ internal const string SendMessagePath = "__hwvSendMessage";
internal const string HybridWebViewDotJsPath = "_framework/hybridwebview.js";
internal const string InvokeDotNetTokenHeaderName = "X-Maui-Invoke-Token";
@@ -115,7 +116,7 @@ public HybridWebViewHandler(IPropertyMapper? mapper = null, CommandMapper? comma
private static bool IsInvokeJavaScriptThrowsExceptionsEnabled =>
!AppContext.TryGetSwitch(InvokeJavaScriptThrowsExceptionsSwitch, out var enabled) || enabled;
- void MessageReceived(string rawMessage)
+ internal void MessageReceived(string rawMessage)
{
if (string.IsNullOrEmpty(rawMessage))
{
@@ -177,7 +178,9 @@ void MessageReceived(string rawMessage)
}
break;
case "__RawMessage":
- VirtualView?.RawMessageReceived(messageContent);
+ // Payload is URL-encoded in JS (HybridWebView.ts sendRawMessage) so it survives
+ // transports that restrict the byte set (Android fetch header forbids CR/LF/NUL).
+ VirtualView?.RawMessageReceived(Uri.UnescapeDataString(messageContent));
break;
default:
throw new ArgumentException($"The message type '{messageType}' is not recognized.", nameof(rawMessage));
diff --git a/src/Core/src/Handlers/MenuFlyoutItem/MenuFlyoutItemHandler.Windows.cs b/src/Core/src/Handlers/MenuFlyoutItem/MenuFlyoutItemHandler.Windows.cs
index 1541e0bff2b3..a9361f8b2078 100644
--- a/src/Core/src/Handlers/MenuFlyoutItem/MenuFlyoutItemHandler.Windows.cs
+++ b/src/Core/src/Handlers/MenuFlyoutItem/MenuFlyoutItemHandler.Windows.cs
@@ -28,8 +28,9 @@ void OnClicked(object sender, UI.Xaml.RoutedEventArgs e)
public static void MapSource(IMenuFlyoutItemHandler handler, IMenuFlyoutItem view)
{
- handler.PlatformView.Icon =
- view.Source?.ToIconSource(handler.MauiContext!)?.CreateIconElement();
+ // Preserve original image colors for MenuFlyoutItem icons (BitmapIconSource renders monochrome by default in WinUI).
+ var iconSource = view.Source?.ToIconSource(handler.MauiContext!, preserveWebColors: true);
+ handler.PlatformView.Icon = iconSource?.CreateIconElement();
}
public static void MapText(IMenuFlyoutItemHandler handler, IMenuFlyoutItem view)
diff --git a/src/Core/src/Handlers/MenuFlyoutSubItem/MenuFlyoutSubItemHandler.Windows.cs b/src/Core/src/Handlers/MenuFlyoutSubItem/MenuFlyoutSubItemHandler.Windows.cs
index 61b6eab3f356..d1dd9f9434dc 100644
--- a/src/Core/src/Handlers/MenuFlyoutSubItem/MenuFlyoutSubItemHandler.Windows.cs
+++ b/src/Core/src/Handlers/MenuFlyoutSubItem/MenuFlyoutSubItemHandler.Windows.cs
@@ -58,8 +58,9 @@ public static void MapIsEnabled(IMenuFlyoutSubItemHandler handler, IMenuFlyoutSu
public static void MapSource(IMenuFlyoutSubItemHandler handler, IMenuFlyoutSubItem view)
{
- handler.PlatformView.Icon =
- view.Source?.ToIconSource(handler.MauiContext!)?.CreateIconElement();
+ // Preserve original image colors for MenuFlyoutSubItem icons (BitmapIconSource renders monochrome by default in WinUI).
+ var iconSource = view.Source?.ToIconSource(handler.MauiContext!, preserveWebColors: true);
+ handler.PlatformView.Icon = iconSource?.CreateIconElement();
}
public override void SetVirtualView(IElement view)
diff --git a/src/Core/src/Handlers/RefreshView/RefreshViewHandler.iOS.cs b/src/Core/src/Handlers/RefreshView/RefreshViewHandler.iOS.cs
index 2b441bb65033..b714d0347296 100644
--- a/src/Core/src/Handlers/RefreshView/RefreshViewHandler.iOS.cs
+++ b/src/Core/src/Handlers/RefreshView/RefreshViewHandler.iOS.cs
@@ -62,7 +62,13 @@ internal static void MapIsRefreshEnabled(IRefreshViewHandler handler, IRefreshVi
=> handler.PlatformView.UpdateIsRefreshEnabled(refreshView.IsRefreshEnabled);
public static void MapIsEnabled(IRefreshViewHandler handler, IRefreshView refreshView)
- => handler.PlatformView.UpdateIsEnabled(refreshView.IsEnabled);
+ {
+ handler.PlatformView!.UpdateIsEnabled(refreshView.IsEnabled);
+
+ // Also funnel through the base handler's IsEnabled mapping so UserInteractionEnabled
+ // stays correctly derived from both IsEnabled and InputTransparent.
+ ViewHandler.MapIsEnabled(handler, refreshView);
+ }
static void UpdateContent(IRefreshViewHandler handler)
{
diff --git a/src/Core/src/Handlers/ScrollView/ScrollViewHandler.Android.cs b/src/Core/src/Handlers/ScrollView/ScrollViewHandler.Android.cs
index 8e24bc8f0daa..d4250379a386 100644
--- a/src/Core/src/Handlers/ScrollView/ScrollViewHandler.Android.cs
+++ b/src/Core/src/Handlers/ScrollView/ScrollViewHandler.Android.cs
@@ -147,6 +147,14 @@ public static void MapOrientation(IScrollViewHandler handler, IScrollView scroll
handler.PlatformView.SetOrientation(scrollView.Orientation);
}
+ internal static void MapFlowDirection(IScrollViewHandler handler, IScrollView scrollView)
+ {
+ if (handler.PlatformView is MauiScrollView mauiScrollView && scrollView is IView view)
+ {
+ mauiScrollView.UpdateFlowDirection(view);
+ }
+ }
+
public static void MapRequestScrollTo(IScrollViewHandler handler, IScrollView scrollView, object? args)
{
if (args is not ScrollToRequest request)
diff --git a/src/Core/src/Handlers/ScrollView/ScrollViewHandler.cs b/src/Core/src/Handlers/ScrollView/ScrollViewHandler.cs
index bff7dc29a9bd..cc46a0771bec 100644
--- a/src/Core/src/Handlers/ScrollView/ScrollViewHandler.cs
+++ b/src/Core/src/Handlers/ScrollView/ScrollViewHandler.cs
@@ -22,6 +22,9 @@ public partial class ScrollViewHandler : IScrollViewHandler
[nameof(IScrollView.HorizontalScrollBarVisibility)] = MapHorizontalScrollBarVisibility,
[nameof(IScrollView.VerticalScrollBarVisibility)] = MapVerticalScrollBarVisibility,
[nameof(IScrollView.Orientation)] = MapOrientation,
+#if ANDROID
+ [nameof(IView.FlowDirection)] = MapFlowDirection,
+#endif
#if __IOS__
[nameof(IScrollView.IsEnabled)] = MapIsEnabled,
#endif
diff --git a/src/Core/src/Handlers/ScrollView/ScrollViewHandler.iOS.cs b/src/Core/src/Handlers/ScrollView/ScrollViewHandler.iOS.cs
index 05daf0c17cf7..ec5ea2d90b82 100644
--- a/src/Core/src/Handlers/ScrollView/ScrollViewHandler.iOS.cs
+++ b/src/Core/src/Handlers/ScrollView/ScrollViewHandler.iOS.cs
@@ -84,6 +84,11 @@ public static void MapContentSize(IScrollViewHandler handler, IScrollView scroll
public static void MapIsEnabled(IScrollViewHandler handler, IScrollView scrollView)
{
handler.PlatformView?.UpdateIsEnabled(scrollView);
+
+ // Also funnel through the base handler's IsEnabled mapping so UserInteractionEnabled
+ // stays correctly derived from both IsEnabled and InputTransparent, not just
+ // ScrollEnabled (which is all the ScrollView-specific overload above sets).
+ ViewHandler.MapIsEnabled(handler, scrollView);
}
public static void MapHorizontalScrollBarVisibility(IScrollViewHandler handler, IScrollView scrollView)
@@ -104,6 +109,12 @@ public static void MapOrientation(IScrollViewHandler handler, IScrollView scroll
}
platformView.UpdateIsEnabled(scrollView);
+
+ // Notify MauiScrollView of orientation change to handle RTL layout
+ if (platformView is MauiScrollView mauiScrollView)
+ {
+ mauiScrollView.OnOrientationChanged();
+ }
platformView.InvalidateMeasure(scrollView);
}
diff --git a/src/Core/src/Handlers/SearchBar/SearchBarHandler.iOS.cs b/src/Core/src/Handlers/SearchBar/SearchBarHandler.iOS.cs
index d4fdacbaa286..7d4f95cd6464 100644
--- a/src/Core/src/Handlers/SearchBar/SearchBarHandler.iOS.cs
+++ b/src/Core/src/Handlers/SearchBar/SearchBarHandler.iOS.cs
@@ -19,7 +19,6 @@ protected override MauiSearchBar CreatePlatformView()
_editor = searchBar.GetSearchTextField();
-
return searchBar;
}
@@ -167,6 +166,9 @@ internal static void MapSelectionLength(ISearchBarHandler handler, ISearchBar se
public static void MapCancelButtonColor(ISearchBarHandler handler, ISearchBar searchBar)
{
handler.PlatformView?.UpdateCancelButton(searchBar);
+ if (handler is SearchBarHandler searchBarHandler)
+ handler.PlatformView?.UpdateClearButtonVisibility(!string.IsNullOrEmpty(searchBar.Text));
+
}
internal static void MapSearchIconColor(ISearchBarHandler handler, ISearchBar searchBar)
@@ -290,6 +292,7 @@ void OnEditingChanged(object? sender, EventArgs e)
if (Handler is SearchBarHandler handler)
{
handler.UpdateCancelButtonVisibility();
+ handler.PlatformView?.UpdateClearButtonVisibility(!string.IsNullOrEmpty(VirtualView?.Text));
}
}
diff --git a/src/Core/src/Handlers/SwipeItemMenuItem/SwipeItemMenuItemHandler.Android.cs b/src/Core/src/Handlers/SwipeItemMenuItem/SwipeItemMenuItemHandler.Android.cs
index a9e32df9821a..0d207b910486 100644
--- a/src/Core/src/Handlers/SwipeItemMenuItem/SwipeItemMenuItemHandler.Android.cs
+++ b/src/Core/src/Handlers/SwipeItemMenuItem/SwipeItemMenuItemHandler.Android.cs
@@ -168,6 +168,7 @@ public override void SetImageSource(Drawable? platformImage)
if (platformImage is not null)
{
var iconSize = GetIconSize(Handler);
+ var textColor = item.GetTextColor()?.ToPlatform();
int drawableWidth = platformImage.IntrinsicWidth;
int drawableHeight = platformImage.IntrinsicHeight;
@@ -184,22 +185,8 @@ public override void SetImageSource(Drawable? platformImage)
platformImage.SetBounds(0, 0, iconWidth, iconHeight);
}
- if (item.Source is IFontImageSource fontImageSource)
- {
- if (fontImageSource.Color is not null)
- {
- platformImage.SetColorFilter(fontImageSource.Color.ToPlatform(), FilterMode.SrcAtop);
- }
- else
- {
- var textColor = item.GetTextColor()?.ToPlatform();
-
- if (textColor is not null)
- {
- platformImage.SetColorFilter(textColor.Value, FilterMode.SrcAtop);
- }
- }
- }
+ if (textColor != null)
+ platformImage.SetColorFilter(textColor.Value, FilterMode.SrcAtop);
}
button.SetCompoundDrawables(null, platformImage, null, null);
diff --git a/src/Core/src/Handlers/SwipeItemMenuItem/SwipeItemMenuItemHandler.Windows.cs b/src/Core/src/Handlers/SwipeItemMenuItem/SwipeItemMenuItemHandler.Windows.cs
index cee48aa05a7c..aca3d0ddb087 100644
--- a/src/Core/src/Handlers/SwipeItemMenuItem/SwipeItemMenuItemHandler.Windows.cs
+++ b/src/Core/src/Handlers/SwipeItemMenuItem/SwipeItemMenuItemHandler.Windows.cs
@@ -1,12 +1,7 @@
-using System;
-using System.Threading.Tasks;
-using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.Logging;
-using Microsoft.UI.Xaml;
+using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Input;
using Microsoft.UI.Xaml.Media;
-using WImageSource = Microsoft.UI.Xaml.Media.ImageSource;
using WSwipeItem = Microsoft.UI.Xaml.Controls.SwipeItem;
namespace Microsoft.Maui.Handlers
@@ -33,7 +28,48 @@ public static void MapText(ISwipeItemMenuItemHandler handler, ISwipeItemMenuItem
public static void MapBackground(ISwipeItemMenuItemHandler handler, ISwipeItemMenuItem view) =>
handler.PlatformView.UpdateBackground(view.Background);
- public static void MapVisibility(ISwipeItemMenuItemHandler handler, ISwipeItemMenuItem view) { }
+ public static void MapVisibility(ISwipeItemMenuItemHandler handler, ISwipeItemMenuItem view)
+ {
+ // WinUI SwipeItem does not support a Visibility property, so we need to
+ // rebuild the parent SwipeView's swipe items to reflect the visibility change.
+ var swipeView = GetParentSwipeView(view);
+ if (swipeView?.Handler is ISwipeViewHandler swipeViewHandler)
+ {
+ if (swipeView.LeftItems?.Contains(view) == true)
+ {
+ swipeViewHandler.UpdateValue(nameof(ISwipeView.LeftItems));
+ }
+ else if (swipeView.RightItems?.Contains(view) == true)
+ {
+ swipeViewHandler.UpdateValue(nameof(ISwipeView.RightItems));
+ }
+ else if (swipeView.TopItems?.Contains(view) == true)
+ {
+ swipeViewHandler.UpdateValue(nameof(ISwipeView.TopItems));
+ }
+ else if (swipeView.BottomItems?.Contains(view) == true)
+ {
+ swipeViewHandler.UpdateValue(nameof(ISwipeView.BottomItems));
+ }
+ }
+ }
+
+ // Walk up the virtual view parent chain to find the owning ISwipeView.
+ // This is more robust than assuming a fixed depth (Parent?.Parent) which
+ // can silently fail if parenting is in transition.
+ static ISwipeView? GetParentSwipeView(IElement? element)
+ {
+ var parent = element?.Parent;
+ while (parent is not null)
+ {
+ if (parent is ISwipeView swipeView)
+ {
+ return swipeView;
+ }
+ parent = parent.Parent;
+ }
+ return null;
+ }
protected override void ConnectHandler(WSwipeItem platformView)
{
@@ -52,38 +88,6 @@ void OnSwipeItemInvoked(WSwipeItem sender, Microsoft.UI.Xaml.Controls.SwipeItemI
VirtualView.OnInvoked();
}
- internal static async Task LoadFileIconAsync(ISwipeItemMenuItemHandler handler, ISwipeItemMenuItem item)
- {
- if (handler.PlatformView is not WSwipeItem swipeItem || handler.MauiContext is null)
- {
- return;
- }
-
- if (item.Source is null)
- {
- swipeItem.IconSource = null;
- return;
- }
-
- var imageSourceServiceProvider = handler.MauiContext.Services.GetRequiredService();
- var scale = handler.MauiContext.GetOptionalPlatformWindow()?.GetDisplayDensity() ?? 1.0f;
- var source = item.Source;
- try
- {
- var service = imageSourceServiceProvider.GetRequiredImageSourceService(source);
- // Do not use ConfigureAwait(false): WinUI DependencyProperty writes require the UI thread.
- var result = await service.GetImageSourceAsync(source, scale);
- if (item.Source == source)
- {
- swipeItem.IconSource = result?.Value is WImageSource platformImage ? new ImageIconSource { ImageSource = platformImage } : null;
- }
- }
- catch (System.Exception ex)
- {
- handler.MauiContext?.CreateLogger()?.Log(LogLevel.Warning, new EventId(), "Cannot load SwipeItem Icon", ex, static (state, _) => state);
- }
- }
-
partial class SwipeItemMenuItemImageSourcePartSetter
{
public override void SetImageSource(ImageSource? platformImage)
diff --git a/src/Core/src/Handlers/SwipeItemMenuItem/SwipeItemMenuItemHandler.cs b/src/Core/src/Handlers/SwipeItemMenuItem/SwipeItemMenuItemHandler.cs
index ef7d53e3d85f..c7389d254ed7 100644
--- a/src/Core/src/Handlers/SwipeItemMenuItem/SwipeItemMenuItemHandler.cs
+++ b/src/Core/src/Handlers/SwipeItemMenuItem/SwipeItemMenuItemHandler.cs
@@ -64,13 +64,13 @@ public static void MapSource(ISwipeItemMenuItemHandler handler, ISwipeItemMenuIt
public static Task MapSourceAsync(ISwipeItemMenuItemHandler handler, ISwipeItemMenuItem image)
{
#if WINDOWS
- return LoadFileIconAsync(handler, image);
+ // TODO: make the mapper use the loader and the image if this is a stream source
+ handler.PlatformView.IconSource = image.Source?.ToIconSource(handler.MauiContext!);
#else
if (handler.SourceLoader is ImageSourcePartLoader loader)
return loader.UpdateImageSourceAsync();
-
- return Task.CompletedTask;
#endif
+ return Task.CompletedTask;
}
partial class SwipeItemMenuItemImageSourcePartSetter : ImageSourcePartSetter
diff --git a/src/Core/src/Handlers/SwipeItemMenuItem/SwipeItemMenuItemHandler.iOS.cs b/src/Core/src/Handlers/SwipeItemMenuItem/SwipeItemMenuItemHandler.iOS.cs
index e20a6cceca24..0eb9faafd22e 100644
--- a/src/Core/src/Handlers/SwipeItemMenuItem/SwipeItemMenuItemHandler.iOS.cs
+++ b/src/Core/src/Handlers/SwipeItemMenuItem/SwipeItemMenuItemHandler.iOS.cs
@@ -119,30 +119,15 @@ public override void SetImageSource(UIImage? platformImage)
try
{
- // Font glyphs are single-color vectors so template rendering + tint makes sense.
- // Regular raster images should use AlwaysOriginal to preserve their own colors.
- var fontImageSource = item.Source as IFontImageSource;
- var renderingMode = fontImageSource is not null ? UIImageRenderingMode.AlwaysTemplate : UIImageRenderingMode.AlwaysOriginal;
- button.SetImage(resizedImage.ImageWithRenderingMode(renderingMode), UIControlState.Normal);
+ button.SetImage(resizedImage.ImageWithRenderingMode(UIImageRenderingMode.AlwaysTemplate), UIControlState.Normal);
- if (fontImageSource is not null)
- {
- if (fontImageSource.Color is not null)
- {
- button.TintColor = fontImageSource.Color.ToPlatform();
- }
- else
- {
- var tintColor = item.GetTextColor();
- if (tintColor is not null)
- {
- button.TintColor = tintColor.ToPlatform();
- }
- }
- }
+ if (item.Source is IFontImageSource fontImageSource && fontImageSource.Color != null)
+ button.TintColor = fontImageSource.Color.ToPlatform();
else
{
- button.TintColor = null;
+ var tintColor = item.GetTextColor();
+ if (tintColor != null)
+ button.TintColor = tintColor.ToPlatform();
}
}
catch (Exception)
diff --git a/src/Core/src/Handlers/SwipeView/SwipeViewHandler.Windows.cs b/src/Core/src/Handlers/SwipeView/SwipeViewHandler.Windows.cs
index fea1902e2dae..76e4ff745251 100644
--- a/src/Core/src/Handlers/SwipeView/SwipeViewHandler.Windows.cs
+++ b/src/Core/src/Handlers/SwipeView/SwipeViewHandler.Windows.cs
@@ -9,6 +9,9 @@ namespace Microsoft.Maui.Handlers
{
public partial class SwipeViewHandler : ViewHandler
{
+ // Guard flag to prevent re-entrancy when CreateSwipeItems calls item.ToHandler(),
+ // which triggers MapVisibility, which calls UpdateValue back into MapLeftItems/MapRightItems.
+ bool _isRebuildingSwipeItems;
protected override WSwipeControl CreatePlatformView() => new();
public override void SetVirtualView(IView view)
@@ -83,11 +86,21 @@ public static void MapLeftItems(ISwipeViewHandler handler, ISwipeView view)
if (!handler.PlatformView.IsLoaded)
return;
+ if (handler is SwipeViewHandler { _isRebuildingSwipeItems: true })
+ {
+ return;
+ }
+
UpdateSwipeItems(SwipeDirection.Left, handler, view, (items) => handler.PlatformView.LeftItems = items, view.LeftItems, handler.PlatformView.LeftItems);
}
public static void MapTopItems(ISwipeViewHandler handler, ISwipeView view)
{
+ if (handler is SwipeViewHandler { _isRebuildingSwipeItems: true })
+ {
+ return;
+ }
+
UpdateSwipeItems(SwipeDirection.Up, handler, view, (items) => handler.PlatformView.TopItems = items, view.TopItems, handler.PlatformView.TopItems);
}
@@ -96,11 +109,21 @@ public static void MapRightItems(ISwipeViewHandler handler, ISwipeView view)
if (!handler.PlatformView.IsLoaded)
return;
+ if (handler is SwipeViewHandler { _isRebuildingSwipeItems: true })
+ {
+ return;
+ }
+
UpdateSwipeItems(SwipeDirection.Right, handler, view, (items) => handler.PlatformView.RightItems = items, view.RightItems, handler.PlatformView.RightItems);
}
public static void MapBottomItems(ISwipeViewHandler handler, ISwipeView view)
{
+ if (handler is SwipeViewHandler { _isRebuildingSwipeItems: true })
+ {
+ return;
+ }
+
UpdateSwipeItems(SwipeDirection.Down, handler, view, (items) => handler.PlatformView.BottomItems = items, view.BottomItems, handler.PlatformView.BottomItems);
}
@@ -178,13 +201,40 @@ static WSwipeItems CreateSwipeItems(SwipeDirection swipeDirection, ISwipeViewHan
swipeItems.Mode = items.Mode.ToPlatform();
- foreach (var item in items)
+ // Set the re-entrancy guard before calling ToHandler() on each item.
+ // ToHandler() triggers initial property mapping, which calls MapVisibility,
+ // which calls UpdateValue back into MapLeftItems/MapRightItems — causing N
+ // redundant rebuilds. The guard prevents those nested calls from re-entering.
+ if (handler is SwipeViewHandler concreteHandler)
{
- if (CanAddSwipeItems(swipeItems) && item is ISwipeItemMenuItem &&
- item.ToHandler(handler.MauiContext!).PlatformView is WSwipeItem swipeItem)
+ concreteHandler._isRebuildingSwipeItems = true;
+ }
+
+ try
+ {
+ foreach (var item in items)
{
- swipeItem.BehaviorOnInvoked = items.SwipeBehaviorOnInvoked.ToPlatform();
- swipeItems.Add(swipeItem);
+ if (item is ISwipeItemMenuItem menuItem)
+ {
+ // Always create the handler regardless of visibility so that subsequent
+ // visibility changes can propagate via the handler's UpdateValue mechanism.
+ var itemElementHandler = item.ToHandler(handler.MauiContext!);
+
+ if (CanAddSwipeItems(swipeItems) &&
+ menuItem.Visibility != Visibility.Collapsed &&
+ itemElementHandler.PlatformView is WSwipeItem swipeItem)
+ {
+ swipeItem.BehaviorOnInvoked = items.SwipeBehaviorOnInvoked.ToPlatform();
+ swipeItems.Add(swipeItem);
+ }
+ }
+ }
+ }
+ finally
+ {
+ if (handler is SwipeViewHandler concreteHandler2)
+ {
+ concreteHandler2._isRebuildingSwipeItems = false;
}
}
diff --git a/src/Core/src/Handlers/View/ViewHandler.cs b/src/Core/src/Handlers/View/ViewHandler.cs
index 85a87f69b3e6..5ae9ef48fc2b 100644
--- a/src/Core/src/Handlers/View/ViewHandler.cs
+++ b/src/Core/src/Handlers/View/ViewHandler.cs
@@ -339,6 +339,9 @@ public static void MapIsEnabled(IViewHandler handler, IView view)
}
#endif
+#if IOS || MACCATALYST
+ MapInputTransparentToContainer(handler, view);
+#endif
((PlatformView?)handler.PlatformView)?.UpdateIsEnabled(view);
}
@@ -531,6 +534,10 @@ public static void MapContainerView(IViewHandler handler, IView view)
else
handler.HasContainer = view.NeedsContainer();
+#if IOS || MACCATALYST
+ MapInputTransparentToContainer(handler, view);
+#endif
+
if (hasContainerOldValue != handler.HasContainer)
{
handler.UpdateValue(nameof(IView.Visibility));
@@ -612,15 +619,22 @@ public static void MapInputTransparent(IViewHandler handler, IView view)
#if IOS || MACCATALYST
// Containers on iOS/Mac Catalyst may be hit testable, so we need to
- // propagate the the view's values to its container view.
- if (handler.ContainerView is WrapperView wrapper)
- wrapper.UpdateInputTransparent(handler, view);
+ // propagate the view's values to its container view.
+ MapInputTransparentToContainer(handler, view);
#endif
((PlatformView?)handler.PlatformView)?.UpdateInputTransparent(handler, view);
#endif
}
+#if IOS || MACCATALYST
+ static void MapInputTransparentToContainer(IViewHandler handler, IView view)
+ {
+ if (handler.ContainerView is WrapperView wrapper)
+ wrapper.UpdateInputTransparent(handler, view);
+ }
+#endif
+
///
/// Maps the abstract method to the platform-specific implementations.
///
diff --git a/src/Core/src/Handlers/View/ViewHandlerOfT.iOS.cs b/src/Core/src/Handlers/View/ViewHandlerOfT.iOS.cs
index 4358d9b7d886..850de7e9fd8a 100644
--- a/src/Core/src/Handlers/View/ViewHandlerOfT.iOS.cs
+++ b/src/Core/src/Handlers/View/ViewHandlerOfT.iOS.cs
@@ -32,6 +32,11 @@ protected override void SetupContainer()
ContainerView ??= new WrapperView(PlatformView.Bounds);
ContainerView.AddSubview(PlatformView);
+ // Re-apply transforms from the cross-platform view model so the wrapper
+ // becomes the transform owner when shadows require a container.
+ ContainerView.UpdateTransformation(VirtualView);
+ PlatformView.ResetLayerTransform();
+
if (oldIndex is int idx && idx >= 0)
oldParent?.InsertSubview(ContainerView, idx);
else
@@ -40,16 +45,28 @@ protected override void SetupContainer()
protected override void RemoveContainer()
{
- if (PlatformView == null || ContainerView == null || PlatformView.Superview != ContainerView)
+ if (PlatformView == null || ContainerView == null)
+ {
+ CleanupContainerView(ContainerView);
+ ContainerView = null;
+ return;
+ }
+
+ if (PlatformView.Superview != ContainerView)
{
CleanupContainerView(ContainerView);
ContainerView = null;
+
+ // Ensure the platform view keeps the current model transform even when
+ // the wrapper was no longer the direct parent.
+ PlatformView.UpdateTransformation(VirtualView);
return;
}
var oldParent = (UIView?)ContainerView.Superview;
var oldIndex = oldParent?.IndexOfSubview(ContainerView);
+
CleanupContainerView(ContainerView);
ContainerView = null;
@@ -58,6 +75,8 @@ protected override void RemoveContainer()
else
oldParent?.AddSubview(PlatformView);
+ PlatformView.UpdateTransformation(VirtualView);
+
void CleanupContainerView(UIView? containerView)
{
if (containerView is WrapperView wrapperView)
diff --git a/src/Core/src/Handlers/WebView/WebViewHandler.Android.cs b/src/Core/src/Handlers/WebView/WebViewHandler.Android.cs
index 12fbc975f6d7..4dff350474a7 100644
--- a/src/Core/src/Handlers/WebView/WebViewHandler.Android.cs
+++ b/src/Core/src/Handlers/WebView/WebViewHandler.Android.cs
@@ -64,6 +64,14 @@ protected override void DisconnectHandler(AWebView platformView)
webChromeClient.Disconnect();
}
+ // Reset layout flag so a stale true value does not trigger ClearHistory()
+ // if this handler is re-connected (e.g., Shell tab switch). (#35788)
+ if (platformView is MauiWebView mauiWebView)
+ {
+ mauiWebView.IsLoadingForLayout = false;
+ }
+
+ platformView.SetWebViewClient(null!);
platformView.SetWebChromeClient(null);
platformView.StopLoading();
diff --git a/src/Core/src/Handlers/WebView/WebViewHandler.iOS.cs b/src/Core/src/Handlers/WebView/WebViewHandler.iOS.cs
index ddc3c99a0d83..6145159d251a 100644
--- a/src/Core/src/Handlers/WebView/WebViewHandler.iOS.cs
+++ b/src/Core/src/Handlers/WebView/WebViewHandler.iOS.cs
@@ -519,28 +519,12 @@ static string GetCookieString(List existingCookies)
bool LoadFile(string url)
{
- try
+ if (PlatformView is null)
{
- var file = Path.GetFileNameWithoutExtension(url);
- var ext = Path.GetExtension(url);
-
- var nsUrl = NSBundle.MainBundle.GetUrlForResource(file, ext);
-
- if (nsUrl == null)
- {
- return false;
- }
-
- PlatformView?.LoadFileUrl(nsUrl, nsUrl);
-
- return true;
- }
- catch (Exception)
- {
- MauiContext?.CreateLogger()?.LogWarning("Could not load {url} as local file", url);
+ return false;
}
- return false;
+ return PlatformView.LoadFile(url, MauiContext?.CreateLogger());
}
public static void MapEvaluateJavaScriptAsync(IWebViewHandler handler, IWebView webView, object? arg)
diff --git a/src/Core/src/Handlers/Window/WindowHandler.Windows.cs b/src/Core/src/Handlers/Window/WindowHandler.Windows.cs
index 2d457bdc50f5..c27c9efd8c7a 100644
--- a/src/Core/src/Handlers/Window/WindowHandler.Windows.cs
+++ b/src/Core/src/Handlers/Window/WindowHandler.Windows.cs
@@ -4,11 +4,16 @@
using Microsoft.UI.Windowing;
using Microsoft.UI.Xaml.Controls;
using Windows.Graphics;
+using WinRT.Interop;
namespace Microsoft.Maui.Handlers
{
public partial class WindowHandler : ElementHandler
{
+ // The HWND never changes after the window is created; cache it once to avoid
+ // a COM/marshalling round-trip on every OnWindowChanged (move/resize) event.
+ IntPtr _hwnd = IntPtr.Zero;
+
protected override void ConnectHandler(UI.Xaml.Window platformView)
{
base.ConnectHandler(platformView);
@@ -20,6 +25,8 @@ protected override void ConnectHandler(UI.Xaml.Window platformView)
platformView.UpdatePosition(VirtualView);
platformView.UpdateSize(VirtualView);
+ _hwnd = platformView.GetWindowHandle();
+
var appWindow = platformView.GetAppWindow();
if (appWindow is not null)
{
@@ -68,6 +75,8 @@ protected override void DisconnectHandler(UI.Xaml.Window platformView)
appWindow.Changed -= OnWindowChanged;
}
+ _hwnd = IntPtr.Zero;
+
base.DisconnectHandler(platformView);
}
@@ -202,9 +211,24 @@ void OnWindowChanged(AppWindow sender, AppWindowChangedEventArgs args)
void UpdateVirtualViewFrame(AppWindow appWindow)
{
+ // Use the HWND cached at ConnectHandler — it never changes and this method
+ // runs on every move/resize event, so avoid the COM round-trip each time.
+ if (_hwnd == IntPtr.Zero)
+ {
+ return;
+ }
+
var size = appWindow.Size;
var pos = appWindow.Position;
+ if (appWindow.Presenter is OverlappedPresenter presenter &&
+ presenter.State == OverlappedPresenterState.Maximized &&
+ PlatformMethods.TryGetExtendedFrameBounds(_hwnd, out var dwmRect))
+ {
+ size = new SizeInt32(dwmRect.Right - dwmRect.Left, dwmRect.Bottom - dwmRect.Top);
+ pos = new PointInt32(dwmRect.Left, dwmRect.Top);
+ }
+
var density = PlatformView.GetDisplayDensity();
VirtualView.FrameChanged(new Rect(
diff --git a/src/Core/src/Hosting/EssentialsMauiAppBuilderExtensions.cs b/src/Core/src/Hosting/EssentialsMauiAppBuilderExtensions.cs
index d18996432efd..d91a61afb484 100644
--- a/src/Core/src/Hosting/EssentialsMauiAppBuilderExtensions.cs
+++ b/src/Core/src/Hosting/EssentialsMauiAppBuilderExtensions.cs
@@ -3,10 +3,20 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Logging;
+using Microsoft.Maui.Accessibility;
using Microsoft.Maui.ApplicationModel;
+using Microsoft.Maui.ApplicationModel.Communication;
+using Microsoft.Maui.ApplicationModel.DataTransfer;
+using MauiContacts = Microsoft.Maui.ApplicationModel.Communication.Contacts;
+using Microsoft.Maui.Authentication;
+using Microsoft.Maui.Devices;
+using Microsoft.Maui.Devices.Sensors;
using Microsoft.Maui.Dispatching;
using Microsoft.Maui.Hosting;
using Microsoft.Maui.LifecycleEvents;
+using Microsoft.Maui.Media;
+using Microsoft.Maui.Networking;
+using Microsoft.Maui.Storage;
#if ANDROID
using Android.App;
#endif
@@ -28,6 +38,26 @@ public static class EssentialsExtensions
{
internal static MauiAppBuilder UseEssentials(this MauiAppBuilder builder)
{
+#if !(ANDROID || __IOS__ || __MACCATALYST__ || WINDOWS || TIZEN)
+ // Register MainThreadBridgeInitializer FIRST so MainThread.SetCustomImplementation
+ // runs before EssentialsInitializer resolves DI-registered services. Order matters:
+ // IMauiInitializeService instances are executed in DI registration order
+ // (MauiContextExtensions.InitializeAppServices iterates GetServices()),
+ // and on netstandard / external TFMs MainThread throws NotImplementedInReferenceAssemblyException
+ // until the bridge is installed. If EssentialsInitializer ran first, any DI-registered
+ // Essentials implementation whose constructor touched MainThread would fail during
+ // the very bridge call meant to enable it.
+ builder.Services.TryAddEnumerable(ServiceDescriptor.Transient());
+#endif
+
+ // Register the EssentialsInitializer unconditionally so DI-registered Essentials
+ // implementations are bridged to the static facades during app startup, even when
+ // ConfigureEssentials() is not called. The initializer's AppActions event handler
+ // is only attached when at least one AppAction handler is configured, to avoid
+ // retaining the initializer instance via the static AppActions.OnAppAction event
+ // for apps that never opt into AppActions.
+ builder.Services.TryAddEnumerable(ServiceDescriptor.Transient());
+
builder.ConfigureLifecycleEvents(life =>
{
#if ANDROID
@@ -83,10 +113,6 @@ internal static MauiAppBuilder UseEssentials(this MauiAppBuilder builder)
#endif
});
-#if !(ANDROID || __IOS__ || __MACCATALYST__ || WINDOWS || TIZEN)
- builder.Services.TryAddEnumerable(ServiceDescriptor.Transient());
-#endif
-
return builder;
}
@@ -163,12 +189,25 @@ public void Initialize(IServiceProvider services)
}
}
+ BridgeEssentialsFromDI(services);
+
#if WINDOWS
- ApplicationModel.Platform.MapServiceToken = _essentialsBuilder.MapServiceToken;
+ // Only forward MapServiceToken when ConfigureEssentials(e => e.UseMapServiceToken(...))
+ // supplied a value. Without this null guard, EssentialsInitializer (now registered
+ // unconditionally) would overwrite any token a caller had set directly via
+ // ApplicationModel.Platform.MapServiceToken before MauiApp.Build().
+ if (_essentialsBuilder.MapServiceToken is not null)
+ ApplicationModel.Platform.MapServiceToken = _essentialsBuilder.MapServiceToken;
#endif
#if !TIZEN
- AppActions.OnAppAction += HandleOnAppAction;
+ // Only subscribe to the static AppActions.OnAppAction event when at least one
+ // handler was actually registered via IEssentialsBuilder.OnAppAction. The static
+ // event subscription would otherwise pin this initializer instance for the app's
+ // lifetime (and across repeated MauiApp.Build() calls in tests / hosting scenarios)
+ // even when the handler is a no-op.
+ if (_essentialsBuilder.AppActionHandlers is not null)
+ AppActions.OnAppAction += HandleOnAppAction;
if (_essentialsBuilder.AppActions is not null)
{
@@ -180,6 +219,106 @@ public void Initialize(IServiceProvider services)
VersionTracking.Track();
}
+ ///
+ /// Bridges DI-registered Essentials implementations to the static facades.
+ /// If a service is registered in DI, it becomes the backing implementation for
+ /// the corresponding static API. If not registered, the existing lazy platform
+ /// default behavior is preserved.
+ ///
+ static void BridgeEssentialsFromDI(IServiceProvider services)
+ {
+ // SetDefault pattern types
+ BridgeIfRegistered(services, Accelerometer.SetDefault);
+ // IActivityStateManager is intentionally NOT bridged. It is Android-only, and its
+ // platform default is already initialized — with its ActivityLifecycleCallbacks
+ // registered — by ApplicationModel.Platform.Init() during UseEssentials(), before this
+ // bridge runs at MauiApp.Build() time. Replacing ActivityStateManager.Default after that
+ // point would leave the original lifecycle listener registered while the replacement
+ // missed the initial Init(Application) call. Custom (non-Android) backends never reach
+ // the Android-only code path, so bridging it here serves no purpose.
+ BridgeIfRegistered(services, Barometer.SetDefault);
+ BridgeIfRegistered(services, Battery.SetDefault);
+ BridgeIfRegistered(services, Browser.SetDefault);
+ BridgeIfRegistered(services, Clipboard.SetDefault);
+ BridgeIfRegistered(services, Compass.SetDefault);
+ BridgeIfRegistered(services, MauiContacts.SetDefault);
+ BridgeIfRegistered(services, Email.SetDefault);
+ BridgeIfRegistered(services, FilePicker.SetDefault);
+ BridgeIfRegistered(services, Flashlight.SetDefault);
+ BridgeIfRegistered(services, Geolocation.SetDefault);
+ BridgeIfRegistered(services, Gyroscope.SetDefault);
+ BridgeIfRegistered(services, HapticFeedback.SetDefault);
+ BridgeIfRegistered(services, Launcher.SetDefault);
+ BridgeIfRegistered(services, Magnetometer.SetDefault);
+ BridgeIfRegistered