diff --git a/.github/ISSUE_TEMPLATE/bug-report.yml b/.github/ISSUE_TEMPLATE/bug-report.yml index 613b8c80f68e..2cc2aaf7d2fc 100644 --- a/.github/ISSUE_TEMPLATE/bug-report.yml +++ b/.github/ISSUE_TEMPLATE/bug-report.yml @@ -42,6 +42,7 @@ body: label: Version with bug description: In what version do you see this issue? Run `dotnet workload list` to find your version. options: + - 10.0.0-rc.2 - 10.0.0-rc.1 - 10.0.0-preview.7 - 10.0.0-preview.6 @@ -50,6 +51,8 @@ body: - 10.0.0-preview.3 - 10.0.0-preview.2 - 10.0.0-preview.1 + - 9.0.110 SR12 + - 9.0.111 SR11.1 - 9.0.110 SR11 - 9.0.100 SR10 - 9.0.90 SR9 @@ -133,6 +136,8 @@ body: - 9.0.90 SR9 - 9.0.100 SR10 - 9.0.110 SR11 + - 9.0.111 SR11.1 + - 9.0.120 SR12 - 10.0.0-preview.1 - 10.0.0-preview.2 - 10.0.0-preview.3 @@ -141,6 +146,7 @@ body: - 10.0.0-preview.6 - 10.0.0-preview.7 - 10.0.0-rc.1 + - 10.0.0-rc.2 validations: required: true - type: dropdown diff --git a/README.md b/README.md index 70386a702084..a2bc3011705b 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,8 @@ * [.NET MAUI Samples](https://github.com/dotnet/maui-samples) * [Development Guide](./.github/DEVELOPMENT.md) +Create a new app with `dotnet new maui -n NewApp` or a [sample app](https://github.com/dotnet/maui-samples/tree/main/10.0/Apps/DeveloperBalance#developer-balance) with `dotnet new maui -n NewApp -sc` that includes the [open-source Syncfusion Toolkit for .NET MAUI](https://www.syncfusion.com/net-maui-toolkit?utm_source=msftdotnet&utm_medium=banner&utm_campaign=mauipremium_sep25) with over 30 additional controls, the [.NET MAUI Community Toolkit](https://github.com/CommunityToolkit/Maui) with tons of helpers and views, and [MVVM Toolkit](https://github.com/CommunityToolkit/dotnet). + ## Overview .NET Multi-platform App UI (.NET MAUI) is the evolution of Xamarin.Forms that expand capabilities beyond mobile Android and iOS into desktop apps for Windows and macOS. With .NET MAUI, you can build apps that perform great on any device that runs Windows, macOS, Android, & iOS from a single codebase. Coupled with Visual Studio productivity tools and emulators, .NET and Visual Studio significantly speed up the development process for building apps that target the widest possible set of devices. Use a single development stack that supports the best-of-breed solutions for all modern workloads with a unified SDK, base class libraries, and a toolchain. [Read More](https://docs.microsoft.com/dotnet/maui/what-is-maui) diff --git a/coverage.runsettings b/coverage.runsettings new file mode 100644 index 000000000000..135d318b972b --- /dev/null +++ b/coverage.runsettings @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/eng/common/core-templates/job/source-build.yml b/eng/common/core-templates/job/source-build.yml index d805d5faeb94..947f0971eb5c 100644 --- a/eng/common/core-templates/job/source-build.yml +++ b/eng/common/core-templates/job/source-build.yml @@ -34,6 +34,9 @@ parameters: # container and pool. platform: {} + # Optional list of directories to ignore for component governance scans. + componentGovernanceIgnoreDirectories: [] + is1ESPipeline: '' # If set to true and running on a non-public project, @@ -94,3 +97,4 @@ jobs: parameters: is1ESPipeline: ${{ parameters.is1ESPipeline }} platform: ${{ parameters.platform }} + componentGovernanceIgnoreDirectories: ${{ parameters.componentGovernanceIgnoreDirectories }} diff --git a/eng/common/core-templates/jobs/source-build.yml b/eng/common/core-templates/jobs/source-build.yml index d92860cba208..eb4b923a7777 100644 --- a/eng/common/core-templates/jobs/source-build.yml +++ b/eng/common/core-templates/jobs/source-build.yml @@ -15,6 +15,9 @@ parameters: # one job runs on 'defaultManagedPlatform'. platforms: [] + # Optional list of directories to ignore for component governance scans. + componentGovernanceIgnoreDirectories: [] + is1ESPipeline: '' # If set to true and running on a non-public project, @@ -31,6 +34,7 @@ jobs: is1ESPipeline: ${{ parameters.is1ESPipeline }} jobNamePrefix: ${{ parameters.jobNamePrefix }} platform: ${{ platform }} + componentGovernanceIgnoreDirectories: ${{ parameters.componentGovernanceIgnoreDirectories }} enableInternalSources: ${{ parameters.enableInternalSources }} - ${{ if eq(length(parameters.platforms), 0) }}: @@ -39,4 +43,5 @@ jobs: is1ESPipeline: ${{ parameters.is1ESPipeline }} jobNamePrefix: ${{ parameters.jobNamePrefix }} platform: ${{ parameters.defaultManagedPlatform }} + componentGovernanceIgnoreDirectories: ${{ parameters.componentGovernanceIgnoreDirectories }} enableInternalSources: ${{ parameters.enableInternalSources }} diff --git a/eng/common/core-templates/steps/get-delegation-sas.yml b/eng/common/core-templates/steps/get-delegation-sas.yml index d2901470a7f0..c6cbea4e8d59 100644 --- a/eng/common/core-templates/steps/get-delegation-sas.yml +++ b/eng/common/core-templates/steps/get-delegation-sas.yml @@ -26,6 +26,7 @@ steps: inputs: azureSubscription: ${{ parameters.federatedServiceConnection }} scriptType: 'pscore' + addSpnToEnvironment: true scriptLocation: 'inlineScript' inlineScript: | # Calculate the expiration of the SAS token and convert to UTC diff --git a/eng/common/core-templates/steps/get-federated-access-token.yml b/eng/common/core-templates/steps/get-federated-access-token.yml index 3a4d4410c482..cff1236fc8d5 100644 --- a/eng/common/core-templates/steps/get-federated-access-token.yml +++ b/eng/common/core-templates/steps/get-federated-access-token.yml @@ -31,6 +31,7 @@ steps: inputs: azureSubscription: ${{ parameters.federatedServiceConnection }} scriptType: 'pscore' + addSpnToEnvironment: true scriptLocation: 'inlineScript' inlineScript: | $accessToken = az account get-access-token --query accessToken --resource ${{ parameters.resource }} --output tsv diff --git a/eng/pipelines/arcade/setup-test-env.yml b/eng/pipelines/arcade/setup-test-env.yml index 655ca1cf7b15..9393f6f4c812 100644 --- a/eng/pipelines/arcade/setup-test-env.yml +++ b/eng/pipelines/arcade/setup-test-env.yml @@ -28,7 +28,11 @@ steps: xcrun xcode-select --print-path xcodebuild -version sudo xcodebuild -license accept - sudo xcodebuild -downloadPlatform iOS + if [[ ${XCODE_VERSION/\.*/} -ge 26 ]]; then + sudo xcodebuild -downloadPlatform iOS -architectureVariant universal + else + sudo xcodebuild -downloadPlatform iOS + fi sudo xcodebuild -runFirstLaunch displayName: Select Xcode Version condition: and(succeeded(), eq(variables['Agent.OS'], 'Darwin')) diff --git a/eng/pipelines/maui-release-internal.yml b/eng/pipelines/maui-release-internal.yml index 06e49fb4b69c..0e4d2d3fd9ce 100644 --- a/eng/pipelines/maui-release-internal.yml +++ b/eng/pipelines/maui-release-internal.yml @@ -74,6 +74,8 @@ extends: ${{ else }}: template: v1/1ES.Unofficial.PipelineTemplate.yml@1ESPipelineTemplates parameters: + settings: + networkIsolationPolicy: Permissive pool: ${{ parameters.VM_IMAGE_HOST }} sdl: binskim: diff --git a/src/Controls/src/Core/Compatibility/Handlers/FlyoutPage/iOS/PhoneFlyoutPageRenderer.cs b/src/Controls/src/Core/Compatibility/Handlers/FlyoutPage/iOS/PhoneFlyoutPageRenderer.cs index 15330f14b416..6c25104d3961 100644 --- a/src/Controls/src/Core/Compatibility/Handlers/FlyoutPage/iOS/PhoneFlyoutPageRenderer.cs +++ b/src/Controls/src/Core/Compatibility/Handlers/FlyoutPage/iOS/PhoneFlyoutPageRenderer.cs @@ -124,6 +124,11 @@ public override void ViewDidAppear(bool animated) { base.ViewDidAppear(animated); Page.SendAppearing(); + if (!_intialLayoutFinished) + { + _intialLayoutFinished = true; + SetInitialPresented(); + } } public override void ViewDidDisappear(bool animated) @@ -158,19 +163,6 @@ void SetInitialPresented() UpdateLeftBarButton(); } - public override void ViewWillLayoutSubviews() - { - // Orientation doesn't seem to be set to a stable correct value until here. - // So, we officially process orientation here. - if (!_intialLayoutFinished) - { - _intialLayoutFinished = true; - SetInitialPresented(); - } - - base.ViewWillLayoutSubviews(); - } - public override void ViewDidLoad() { base.ViewDidLoad(); diff --git a/src/Controls/src/Core/Compatibility/Handlers/NavigationPage/iOS/NavigationRenderer.cs b/src/Controls/src/Core/Compatibility/Handlers/NavigationPage/iOS/NavigationRenderer.cs index a99761006d73..b9395408034d 100644 --- a/src/Controls/src/Core/Compatibility/Handlers/NavigationPage/iOS/NavigationRenderer.cs +++ b/src/Controls/src/Core/Compatibility/Handlers/NavigationPage/iOS/NavigationRenderer.cs @@ -2089,7 +2089,13 @@ class Container : UIView public Container(View view, UINavigationBar bar) : base(bar.Bounds) { - if (OperatingSystem.IsIOSVersionAtLeast(11) || OperatingSystem.IsMacCatalystVersionAtLeast(11)) + // For iOS 26+, we need to use autoresizing masks instead of constraints to ensure proper TitleView display + if (OperatingSystem.IsIOSVersionAtLeast(26) || OperatingSystem.IsMacCatalystVersionAtLeast(26)) + { + TranslatesAutoresizingMaskIntoConstraints = true; + AutoresizingMask = UIViewAutoresizing.FlexibleHeight | UIViewAutoresizing.FlexibleWidth; + } + else if (OperatingSystem.IsIOSVersionAtLeast(11) || OperatingSystem.IsMacCatalystVersionAtLeast(11)) { TranslatesAutoresizingMaskIntoConstraints = false; } @@ -2178,7 +2184,9 @@ public override CGRect Frame { if (Superview != null) { - if (!(OperatingSystem.IsIOSVersionAtLeast(11) || OperatingSystem.IsMacCatalystVersionAtLeast(11))) + // For iOS 26+ and pre-iOS 11, we use autoresizing masks and need to adjust the frame manually + if (OperatingSystem.IsIOSVersionAtLeast(26) || OperatingSystem.IsMacCatalystVersionAtLeast(26) || + !(OperatingSystem.IsIOSVersionAtLeast(11) || OperatingSystem.IsMacCatalystVersionAtLeast(11))) { value.Y = Superview.Bounds.Y; diff --git a/src/Controls/src/Core/Handlers/Items/StructuredItemsViewHandler.Windows.cs b/src/Controls/src/Core/Handlers/Items/StructuredItemsViewHandler.Windows.cs index e3315ac88ed8..09b8636591c6 100644 --- a/src/Controls/src/Core/Handlers/Items/StructuredItemsViewHandler.Windows.cs +++ b/src/Controls/src/Core/Handlers/Items/StructuredItemsViewHandler.Windows.cs @@ -18,6 +18,10 @@ public partial class StructuredItemsViewHandler : ItemsViewHandler _layoutPropertyChangedProxy?.Unsubscribe(); @@ -79,6 +83,9 @@ public static void MapItemSizingStrategy(StructuredItemsViewHandler protected override ListViewBase SelectListViewBase() { + _listViewItemStyle = GetDefaultStyle(ListViewItemStyleKey); + _gridViewItemStyle = GetDefaultStyle(GridViewItemStyleKey); + switch (VirtualView.ItemsLayout) { case GridItemsLayout gridItemsLayout: @@ -242,6 +249,11 @@ static WStyle GetItemContainerStyle(GridItemsLayout layout) var style = new WStyle(typeof(GridViewItem)); + if (_gridViewItemStyle is not null) + { + style.BasedOn = _gridViewItemStyle; + } + style.Setters.Add(new WSetter(FrameworkElement.MarginProperty, margin)); style.Setters.Add(new WSetter(Control.PaddingProperty, WinUIHelpers.CreateThickness(0))); style.Setters.Add(new WSetter(Control.HorizontalContentAlignmentProperty, HorizontalAlignment.Stretch)); @@ -249,6 +261,11 @@ static WStyle GetItemContainerStyle(GridItemsLayout layout) return style; } + static WStyle GetDefaultStyle(string resourceKey) + { + return Microsoft.UI.Xaml.Application.Current.Resources[resourceKey] as WStyle; + } + static WStyle GetVerticalItemContainerStyle(LinearItemsLayout layout) { var v = layout?.ItemSpacing ?? 0; @@ -256,6 +273,11 @@ static WStyle GetVerticalItemContainerStyle(LinearItemsLayout layout) var style = new WStyle(typeof(ListViewItem)); + if (_listViewItemStyle is not null) + { + style.BasedOn = _listViewItemStyle; + } + style.Setters.Add(new WSetter(FrameworkElement.MinHeightProperty, 0)); style.Setters.Add(new WSetter(FrameworkElement.MarginProperty, margin)); style.Setters.Add(new WSetter(Control.PaddingProperty, WinUIHelpers.CreateThickness(0))); @@ -271,6 +293,11 @@ static WStyle GetHorizontalItemContainerStyle(LinearItemsLayout layout) var style = new WStyle(typeof(ListViewItem)); + if (_listViewItemStyle is not null) + { + style.BasedOn = _listViewItemStyle; + } + style.Setters.Add(new WSetter(FrameworkElement.MinWidthProperty, 0)); style.Setters.Add(new WSetter(Control.PaddingProperty, padding)); style.Setters.Add(new WSetter(Control.VerticalContentAlignmentProperty, VerticalAlignment.Stretch)); diff --git a/src/Controls/src/Core/Items/CarouselLayoutTypeConverter.cs b/src/Controls/src/Core/Items/CarouselLayoutTypeConverter.cs index 9bb0fe170d57..35621216186c 100644 --- a/src/Controls/src/Core/Items/CarouselLayoutTypeConverter.cs +++ b/src/Controls/src/Core/Items/CarouselLayoutTypeConverter.cs @@ -19,12 +19,12 @@ public override bool CanConvertTo(ITypeDescriptorContext? context, Type? destina if (strValue == "HorizontalList") { - return LinearItemsLayout.CarouselDefault; + return LinearItemsLayout.CreateCarouselHorizontalDefault(); } if (strValue == "VerticalList") { - return LinearItemsLayout.CarouselVertical; + return LinearItemsLayout.CreateCarouselVerticalDefault(); } throw new InvalidOperationException($"Cannot convert \"{strValue}\" into {typeof(LinearItemsLayout)}"); diff --git a/src/Controls/src/Core/Items/CarouselView.cs b/src/Controls/src/Core/Items/CarouselView.cs index 95918e8efc3a..c83e81a7ce1d 100644 --- a/src/Controls/src/Core/Items/CarouselView.cs +++ b/src/Controls/src/Core/Items/CarouselView.cs @@ -187,7 +187,7 @@ public object PositionChangedCommandParameter /// Bindable property for . public static readonly BindableProperty ItemsLayoutProperty = BindableProperty.Create(nameof(ItemsLayout), typeof(LinearItemsLayout), typeof(ItemsView), - LinearItemsLayout.CarouselDefault); + null, defaultValueCreator: (b) => LinearItemsLayout.CreateCarouselHorizontalDefault()); /// [System.ComponentModel.TypeConverter(typeof(CarouselLayoutTypeConverter))] diff --git a/src/Controls/src/Core/Items/ItemsLayoutTypeConverter.cs b/src/Controls/src/Core/Items/ItemsLayoutTypeConverter.cs index dc5f639d74a1..f65954431530 100644 --- a/src/Controls/src/Core/Items/ItemsLayoutTypeConverter.cs +++ b/src/Controls/src/Core/Items/ItemsLayoutTypeConverter.cs @@ -26,11 +26,11 @@ public override bool CanConvertTo(ITypeDescriptorContext? context, Type? destina if (strValue == "VerticalList") { - return LinearItemsLayout.Vertical; + return LinearItemsLayout.CreateVerticalDefault(); } else if (strValue == "HorizontalList") { - return LinearItemsLayout.Horizontal; + return LinearItemsLayout.CreateHorizontalDefault(); } else if (strValue.StartsWith("VerticalGrid", StringComparison.Ordinal)) { diff --git a/src/Controls/src/Core/Items/ItemsView.cs b/src/Controls/src/Core/Items/ItemsView.cs index fab7d27fd467..ca4f5b01ccc6 100644 --- a/src/Controls/src/Core/Items/ItemsView.cs +++ b/src/Controls/src/Core/Items/ItemsView.cs @@ -113,7 +113,8 @@ public int RemainingItemsThreshold internal static readonly BindableProperty InternalItemsLayoutProperty = BindableProperty.Create(nameof(ItemsLayout), typeof(IItemsLayout), typeof(ItemsView), - LinearItemsLayout.Vertical, propertyChanged: OnInternalItemsLayoutPropertyChanged); + null, propertyChanged: OnInternalItemsLayoutPropertyChanged, + defaultValueCreator: (b) => LinearItemsLayout.CreateVerticalDefault()); static void OnInternalItemsLayoutPropertyChanged(BindableObject bindable, object oldValue, object newValue) { diff --git a/src/Controls/src/Core/Items/LinearItemsLayout.cs b/src/Controls/src/Core/Items/LinearItemsLayout.cs index fc08ecc343c1..db4d46c9c1bc 100644 --- a/src/Controls/src/Core/Items/LinearItemsLayout.cs +++ b/src/Controls/src/Core/Items/LinearItemsLayout.cs @@ -12,22 +12,14 @@ public LinearItemsLayout([Parameter("Orientation")] ItemsLayoutOrientation orien } /// - public static readonly IItemsLayout Vertical = new LinearItemsLayout(ItemsLayoutOrientation.Vertical); + public static readonly IItemsLayout Vertical = CreateVerticalDefault(); /// - public static readonly IItemsLayout Horizontal = new LinearItemsLayout(ItemsLayoutOrientation.Horizontal); + public static readonly IItemsLayout Horizontal = CreateHorizontalDefault(); /// - public static readonly IItemsLayout CarouselVertical = new LinearItemsLayout(ItemsLayoutOrientation.Vertical) - { - SnapPointsType = SnapPointsType.MandatorySingle, - SnapPointsAlignment = SnapPointsAlignment.Center - }; + public static readonly IItemsLayout CarouselVertical = CreateCarouselVerticalDefault(); - internal static readonly LinearItemsLayout CarouselDefault = new LinearItemsLayout(ItemsLayoutOrientation.Horizontal) - { - SnapPointsType = SnapPointsType.MandatorySingle, - SnapPointsAlignment = SnapPointsAlignment.Center - }; + internal static readonly LinearItemsLayout CarouselDefault = CreateCarouselHorizontalDefault(); /// Bindable property for . public static readonly BindableProperty ItemSpacingProperty = @@ -40,5 +32,25 @@ public double ItemSpacing get => (double)GetValue(ItemSpacingProperty); set => SetValue(ItemSpacingProperty, value); } + + internal static LinearItemsLayout CreateVerticalDefault() + => new LinearItemsLayout(ItemsLayoutOrientation.Vertical); + + internal static LinearItemsLayout CreateHorizontalDefault() + => new LinearItemsLayout(ItemsLayoutOrientation.Horizontal); + + internal static LinearItemsLayout CreateCarouselVerticalDefault() + => new LinearItemsLayout(ItemsLayoutOrientation.Vertical) + { + SnapPointsType = SnapPointsType.MandatorySingle, + SnapPointsAlignment = SnapPointsAlignment.Center + }; + + internal static LinearItemsLayout CreateCarouselHorizontalDefault() + => new LinearItemsLayout(ItemsLayoutOrientation.Horizontal) + { + SnapPointsType = SnapPointsType.MandatorySingle, + SnapPointsAlignment = SnapPointsAlignment.Center + }; } } \ No newline at end of file diff --git a/src/Controls/src/Core/PublicAPI/net-ios/PublicAPI.Unshipped.txt b/src/Controls/src/Core/PublicAPI/net-ios/PublicAPI.Unshipped.txt index 869def3ea434..dd9b4164a2a2 100644 --- a/src/Controls/src/Core/PublicAPI/net-ios/PublicAPI.Unshipped.txt +++ b/src/Controls/src/Core/PublicAPI/net-ios/PublicAPI.Unshipped.txt @@ -1,4 +1,5 @@ -#nullable enable +#nullable enable +*REMOVED*override Microsoft.Maui.Controls.Handlers.Compatibility.PhoneFlyoutPageRenderer.ViewWillLayoutSubviews() -> void *REMOVED*Microsoft.Maui.Controls.Accelerator *REMOVED*~Microsoft.Maui.Controls.Accelerator.Keys.get -> System.Collections.Generic.IEnumerable *REMOVED*~Microsoft.Maui.Controls.Accelerator.Keys.set -> void diff --git a/src/Controls/src/Core/PublicAPI/net-maccatalyst/PublicAPI.Unshipped.txt b/src/Controls/src/Core/PublicAPI/net-maccatalyst/PublicAPI.Unshipped.txt index 869def3ea434..dd9b4164a2a2 100644 --- a/src/Controls/src/Core/PublicAPI/net-maccatalyst/PublicAPI.Unshipped.txt +++ b/src/Controls/src/Core/PublicAPI/net-maccatalyst/PublicAPI.Unshipped.txt @@ -1,4 +1,5 @@ -#nullable enable +#nullable enable +*REMOVED*override Microsoft.Maui.Controls.Handlers.Compatibility.PhoneFlyoutPageRenderer.ViewWillLayoutSubviews() -> void *REMOVED*Microsoft.Maui.Controls.Accelerator *REMOVED*~Microsoft.Maui.Controls.Accelerator.Keys.get -> System.Collections.Generic.IEnumerable *REMOVED*~Microsoft.Maui.Controls.Accelerator.Keys.set -> void diff --git a/src/Controls/tests/Core.UnitTests/ItemsLayoutTypeConverterTests.cs b/src/Controls/tests/Core.UnitTests/ItemsLayoutTypeConverterTests.cs index 63dc04ab9507..c63229840874 100644 --- a/src/Controls/tests/Core.UnitTests/ItemsLayoutTypeConverterTests.cs +++ b/src/Controls/tests/Core.UnitTests/ItemsLayoutTypeConverterTests.cs @@ -11,7 +11,10 @@ public void HorizontalListShouldReturnLinearItemsLayout() { var converter = new ItemsLayoutTypeConverter(); var result = converter.ConvertFromInvariantString("HorizontalList"); - Assert.Same(LinearItemsLayout.Horizontal, result); + + Assert.IsType(result); + var linearLayout = (LinearItemsLayout)result; + Assert.Equal(ItemsLayoutOrientation.Horizontal, linearLayout.Orientation); } [Fact] @@ -19,7 +22,10 @@ public void VerticalListShouldReturnLinearItemsLayout() { var converter = new ItemsLayoutTypeConverter(); var result = converter.ConvertFromInvariantString("VerticalList"); - Assert.Same(LinearItemsLayout.Vertical, result); + + Assert.IsType(result); + var linearLayout = (LinearItemsLayout)result; + Assert.Equal(ItemsLayoutOrientation.Vertical, linearLayout.Orientation); } [Fact] diff --git a/src/Controls/tests/DeviceTests/Elements/CarouselView/CarouselViewTests.iOS.cs b/src/Controls/tests/DeviceTests/Elements/CarouselView/CarouselViewTests.iOS.cs index 97ab3a259670..7675d1dd2fc8 100644 --- a/src/Controls/tests/DeviceTests/Elements/CarouselView/CarouselViewTests.iOS.cs +++ b/src/Controls/tests/DeviceTests/Elements/CarouselView/CarouselViewTests.iOS.cs @@ -1,7 +1,54 @@ -namespace Microsoft.Maui.DeviceTests +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Microsoft.Maui.Controls; +using Microsoft.Maui.Controls.Handlers.Items2; +using Microsoft.Maui.Handlers; +using Xunit; + +namespace Microsoft.Maui.DeviceTests { public partial class CarouselViewTests { + [Fact(DisplayName = "CarouselView Does Not Leak With Default ItemsLayout")] + public async Task CarouselViewDoesNotLeakWithDefaultItemsLayout() + { + SetupBuilder(); + + WeakReference weakCarouselView = null; + WeakReference weakHandler = null; + + await InvokeOnMainThreadAsync(async () => + { + var carouselView = new CarouselView + { + ItemsSource = new List { "Item 1", "Item 2", "Item 3" }, + ItemTemplate = new DataTemplate(() => new Label()) + // Note: Not setting ItemsLayout - using the default + }; + + weakCarouselView = new WeakReference(carouselView); + + var handler = await CreateHandlerAsync(carouselView); + + // Verify handler is created + Assert.NotNull(handler); + + // Store weak reference to the handler + weakHandler = new WeakReference(handler); + + // Disconnect the handler + ((IElementHandler)handler).DisconnectHandler(); + }); + + // Force garbage collection + await AssertionExtensions.WaitForGC(weakCarouselView, weakHandler); + + // Verify the CarouselView was collected + Assert.False(weakCarouselView.IsAlive, "CarouselView should have been garbage collected"); + // Verify the handler was collected + Assert.False(weakHandler.IsAlive, "CarouselViewHandler2 should have been garbage collected"); + } } } \ No newline at end of file diff --git a/src/Controls/tests/DeviceTests/Elements/CollectionView/CollectionViewTests.iOS.cs b/src/Controls/tests/DeviceTests/Elements/CollectionView/CollectionViewTests.iOS.cs index 431454c9d4b6..1a9dfd28b7ce 100644 --- a/src/Controls/tests/DeviceTests/Elements/CollectionView/CollectionViewTests.iOS.cs +++ b/src/Controls/tests/DeviceTests/Elements/CollectionView/CollectionViewTests.iOS.cs @@ -251,6 +251,47 @@ public void IndexPathValidTest() Assert.False(source.IsIndexPathValid(invalidSection)); } + [Fact(DisplayName = "CollectionView Does Not Leak With Default ItemsLayout")] + public async Task CollectionViewDoesNotLeakWithDefaultItemsLayout() + { + SetupBuilder(); + + WeakReference weakCollectionView = null; + WeakReference weakHandler = null; + + await InvokeOnMainThreadAsync(async () => + { + var collectionView = new CollectionView + { + ItemsSource = new List { "Item 1", "Item 2", "Item 3" }, + ItemTemplate = new DataTemplate(() => new Label()) + // Note: Not setting ItemsLayout - using the default + }; + + weakCollectionView = new WeakReference(collectionView); + + var handler = await CreateHandlerAsync(collectionView); + + // Verify handler is created + Assert.NotNull(handler); + + // Store weak reference to the handler + weakHandler = new WeakReference(handler); + + // Disconnect the handler + ((IElementHandler)handler).DisconnectHandler(); + }); + + // Force garbage collection + await AssertionExtensions.WaitForGC(weakCollectionView, weakHandler); + + // Verify the CollectionView was collected + Assert.False(weakCollectionView.IsAlive, "CollectionView should have been garbage collected"); + + // Verify the handler was collected + Assert.False(weakHandler.IsAlive, "CollectionViewHandler2 should have been garbage collected"); + } + /// /// Simulates what a developer might do with a Page/View /// diff --git a/src/Controls/tests/SourceGen.UnitTests/CultureTestsCollection.cs b/src/Controls/tests/SourceGen.UnitTests/CultureTestsCollection.cs new file mode 100644 index 000000000000..40716785eba2 --- /dev/null +++ b/src/Controls/tests/SourceGen.UnitTests/CultureTestsCollection.cs @@ -0,0 +1,8 @@ +using Xunit; + +namespace Microsoft.Maui.Controls.SourceGen.UnitTests; + +[CollectionDefinition("CultureTests", DisableParallelization = true)] +public class CultureTestsCollection +{ +} diff --git a/src/Controls/tests/SourceGen.UnitTests/InitializeComponent/BasicCase.cs b/src/Controls/tests/SourceGen.UnitTests/InitializeComponent/BasicCase.cs index 9ebc4b46ff6f..53079c485dfa 100644 --- a/src/Controls/tests/SourceGen.UnitTests/InitializeComponent/BasicCase.cs +++ b/src/Controls/tests/SourceGen.UnitTests/InitializeComponent/BasicCase.cs @@ -1,11 +1,11 @@ using System.Linq; -using NUnit.Framework; +using Xunit; namespace Microsoft.Maui.Controls.SourceGen.UnitTests; public class BasicCase : SourceGenXamlInitializeComponentTestBase { - [Test] + [Fact] public void BasicXaml() { var xaml = @@ -84,8 +84,8 @@ private partial void InitializeComponent() """; var (result, generated) = RunGenerator(xaml, code); - Assert.IsFalse(result.Diagnostics.Any()); + Assert.False(result.Diagnostics.Any()); - Assert.AreEqual(expected, generated); + Assert.Equal(expected, generated); } } diff --git a/src/Controls/tests/SourceGen.UnitTests/InitializeComponent/CompiledBindings.cs b/src/Controls/tests/SourceGen.UnitTests/InitializeComponent/CompiledBindings.cs index ad78118bacf0..5e85319f1595 100644 --- a/src/Controls/tests/SourceGen.UnitTests/InitializeComponent/CompiledBindings.cs +++ b/src/Controls/tests/SourceGen.UnitTests/InitializeComponent/CompiledBindings.cs @@ -1,13 +1,13 @@ using System; using System.IO; using System.Linq; -using NUnit.Framework; +using Xunit; namespace Microsoft.Maui.Controls.SourceGen.UnitTests; public class CompiledBindings : SourceGenXamlInitializeComponentTestBase { - [Test] + [Fact] public void CanDetectXDataType() { var xaml = @@ -160,7 +160,7 @@ static bool ShouldUseSetter(global::Microsoft.Maui.Controls.BindingMode mode) """; var (result, generated) = RunGenerator(xaml, code); - Assert.IsFalse(result.Diagnostics.Any()); - Assert.AreEqual(expected, generated); + Assert.False(result.Diagnostics.Any()); + Assert.Equal(expected, generated); } } diff --git a/src/Controls/tests/SourceGen.UnitTests/InitializeComponent/DoesNotFail.cs b/src/Controls/tests/SourceGen.UnitTests/InitializeComponent/DoesNotFail.cs index f30637a3ef6d..c8a747642d6c 100644 --- a/src/Controls/tests/SourceGen.UnitTests/InitializeComponent/DoesNotFail.cs +++ b/src/Controls/tests/SourceGen.UnitTests/InitializeComponent/DoesNotFail.cs @@ -1,11 +1,11 @@ using System.Linq; -using NUnit.Framework; +using Xunit; namespace Microsoft.Maui.Controls.SourceGen.UnitTests; public class DoesNotFail : SourceGenXamlInitializeComponentTestBase { - [Test] + [Fact] public void Test() { var xaml = @@ -48,7 +48,7 @@ public TestPage() """; var (result, generated) = RunGenerator(xaml, code); - Assert.IsFalse(result.Diagnostics.Any()); + Assert.False(result.Diagnostics.Any()); // Assert.AreEqual(expected, generated); } diff --git a/src/Controls/tests/SourceGen.UnitTests/InitializeComponent/LineInfoTests.cs b/src/Controls/tests/SourceGen.UnitTests/InitializeComponent/LineInfoTests.cs index 414fb623b245..9d734051b192 100644 --- a/src/Controls/tests/SourceGen.UnitTests/InitializeComponent/LineInfoTests.cs +++ b/src/Controls/tests/SourceGen.UnitTests/InitializeComponent/LineInfoTests.cs @@ -1,13 +1,13 @@ using System; using System.IO; using System.Linq; -using NUnit.Framework; +using Xunit; namespace Microsoft.Maui.Controls.SourceGen.UnitTests; public class LineInfoTests : SourceGenXamlInitializeComponentTestBase { - [Test] + [Fact] public void DiagnosticShowsLocationInInputXamlFile() { var xaml = @@ -31,6 +31,6 @@ public void DiagnosticShowsLocationInInputXamlFile() var generatedCode = result.GeneratedTrees.Single(tree => Path.GetFileName(tree.FilePath) == "Test.xaml.xsg.cs").ToString(); var expectedFilePath = Path.Combine(Environment.CurrentDirectory, "Test.xaml"); - Assert.IsTrue(generatedCode.Contains(@$"#line 9 ""{expectedFilePath}""", StringComparison.Ordinal)); + Assert.True(generatedCode.Contains(@$"#line 9 ""{expectedFilePath}""", StringComparison.Ordinal)); } } diff --git a/src/Controls/tests/SourceGen.UnitTests/InitializeComponent/ResourceDictionary.cs b/src/Controls/tests/SourceGen.UnitTests/InitializeComponent/ResourceDictionary.cs index 2ebccb583076..638abd4d7595 100644 --- a/src/Controls/tests/SourceGen.UnitTests/InitializeComponent/ResourceDictionary.cs +++ b/src/Controls/tests/SourceGen.UnitTests/InitializeComponent/ResourceDictionary.cs @@ -1,11 +1,11 @@ using System.Linq; -using NUnit.Framework; +using Xunit; namespace Microsoft.Maui.Controls.SourceGen.UnitTests; public class ResourceDictionary : SourceGenXamlInitializeComponentTestBase { - [Test] + [Fact] public void ResourceDictionaryWithoutXClass() { var xaml = @@ -51,9 +51,9 @@ private partial void InitializeComponent() """; var (result, generated) = RunGenerator(xaml, "", path: "Styles.xaml"); - Assert.IsFalse(result.Diagnostics.Any()); + Assert.False(result.Diagnostics.Any()); - Assert.AreEqual(expected, generated); + Assert.Equal(expected, generated); } } diff --git a/src/Controls/tests/SourceGen.UnitTests/InitializeComponent/SetBinding.cs b/src/Controls/tests/SourceGen.UnitTests/InitializeComponent/SetBinding.cs index ca8ea28a03ad..5201064c6e91 100644 --- a/src/Controls/tests/SourceGen.UnitTests/InitializeComponent/SetBinding.cs +++ b/src/Controls/tests/SourceGen.UnitTests/InitializeComponent/SetBinding.cs @@ -1,13 +1,13 @@ using System; using System.IO; using System.Linq; -using NUnit.Framework; +using Xunit; namespace Microsoft.Maui.Controls.SourceGen.UnitTests; public class SetBinding : SourceGenXamlInitializeComponentTestBase { - [Test] + [Fact] public void Test() { var xaml = @@ -81,7 +81,7 @@ private partial void InitializeComponent() """; var (result, generated) = RunGenerator(xaml, code); - Assert.IsFalse(result.Diagnostics.Any()); - Assert.AreEqual(expected, generated); + Assert.False(result.Diagnostics.Any()); + Assert.Equal(expected, generated); } } \ No newline at end of file diff --git a/src/Controls/tests/SourceGen.UnitTests/InitializeComponent/SetValue.cs b/src/Controls/tests/SourceGen.UnitTests/InitializeComponent/SetValue.cs index efe409a667d8..4dd70c7dfb4c 100644 --- a/src/Controls/tests/SourceGen.UnitTests/InitializeComponent/SetValue.cs +++ b/src/Controls/tests/SourceGen.UnitTests/InitializeComponent/SetValue.cs @@ -1,12 +1,12 @@ using System.Linq; using Microsoft.Maui.Controls.Xaml.UnitTests.SourceGen; -using NUnit.Framework; +using Xunit; namespace Microsoft.Maui.Controls.SourceGen.UnitTests; public class SetValue : SourceGenXamlInitializeComponentTestBase { - [Test] + [Fact] public void Test() { var xaml = @@ -39,7 +39,7 @@ public TestPage() """; var (result, generated) = RunGenerator(xaml, code); - Assert.IsFalse(result.Diagnostics.Any()); + Assert.False(result.Diagnostics.Any()); } } \ No newline at end of file diff --git a/src/Controls/tests/SourceGen.UnitTests/InitializeComponent/SimplifyOnPlatform.cs b/src/Controls/tests/SourceGen.UnitTests/InitializeComponent/SimplifyOnPlatform.cs index d604603923d3..04c61499ee90 100644 --- a/src/Controls/tests/SourceGen.UnitTests/InitializeComponent/SimplifyOnPlatform.cs +++ b/src/Controls/tests/SourceGen.UnitTests/InitializeComponent/SimplifyOnPlatform.cs @@ -1,13 +1,13 @@ using System; using System.IO; using System.Linq; -using NUnit.Framework; +using Xunit; namespace Microsoft.Maui.Controls.SourceGen.UnitTests; public class SimplifyOnPlatform : SourceGenXamlInitializeComponentTestBase { - [Test] + [Fact] public void Test() { var xaml = @@ -166,8 +166,8 @@ private partial void InitializeComponent() """; var (result, generated) = RunGenerator(xaml, code, targetFramework: "net10.0-android"); - Assert.IsFalse(result.Diagnostics.Any()); + Assert.False(result.Diagnostics.Any()); - Assert.AreEqual(expected, generated); + Assert.Equal(expected, generated); } } \ No newline at end of file diff --git a/src/Controls/tests/SourceGen.UnitTests/InitializeComponent/WarningIgnore.cs b/src/Controls/tests/SourceGen.UnitTests/InitializeComponent/WarningIgnore.cs index 2044bddffce6..baf02d4a1b41 100644 --- a/src/Controls/tests/SourceGen.UnitTests/InitializeComponent/WarningIgnore.cs +++ b/src/Controls/tests/SourceGen.UnitTests/InitializeComponent/WarningIgnore.cs @@ -1,11 +1,11 @@ using System.Linq; -using NUnit.Framework; +using Xunit; namespace Microsoft.Maui.Controls.SourceGen.UnitTests; public class CSWarningIgnore : SourceGenXamlInitializeComponentTestBase { - [Test] + [Fact] public void WarningIgnore() { var xaml = @@ -86,8 +86,8 @@ private partial void InitializeComponent() """; var (result, generated) = RunGenerator(xaml, code, "0168, CS0612"); - Assert.IsFalse(result.Diagnostics.Any()); + Assert.False(result.Diagnostics.Any()); - Assert.AreEqual(expected, generated); + Assert.Equal(expected, generated); } } \ No newline at end of file diff --git a/src/Controls/tests/SourceGen.UnitTests/KnownTypeConvertersTests.cs b/src/Controls/tests/SourceGen.UnitTests/KnownTypeConvertersTests.cs index aa3e65f490e4..e0b47aaefd0f 100644 --- a/src/Controls/tests/SourceGen.UnitTests/KnownTypeConvertersTests.cs +++ b/src/Controls/tests/SourceGen.UnitTests/KnownTypeConvertersTests.cs @@ -1,26 +1,24 @@ +using System; using System.Globalization; -using NUnit.Framework; using Microsoft.CodeAnalysis; using System.Linq; +using Xunit; namespace Microsoft.Maui.Controls.SourceGen.UnitTests { - [TestFixture] - [NonParallelizable] - public class KnownTypeConvertersTests : SourceGenXamlInitializeComponentTestBase + [Collection("CultureTests")] + public class KnownTypeConvertersTests : SourceGenXamlInitializeComponentTestBase, IDisposable { - private CultureInfo? _originalCulture; - private CultureInfo? _originalUICulture; + private readonly CultureInfo? _originalCulture; + private readonly CultureInfo? _originalUICulture; - [SetUp] - public void SetUp() + public KnownTypeConvertersTests() { _originalCulture = System.Threading.Thread.CurrentThread.CurrentCulture; _originalUICulture = System.Threading.Thread.CurrentThread.CurrentUICulture; } - [TearDown] - public void TearDown() + void IDisposable.Dispose() { if (_originalCulture is not null) { @@ -33,11 +31,12 @@ public void TearDown() } } - [TestCase("en-US", "1.5*")] - [TestCase("fr-FR", "2.5*")] - [TestCase("de-DE", "3.14*")] - [TestCase("es-ES", "0.75*")] - [TestCase("ru-RU", "1.414*")] + [Theory] + [InlineData("en-US", "1.5*")] + [InlineData("fr-FR", "2.5*")] + [InlineData("de-DE", "3.14*")] + [InlineData("es-ES", "0.75*")] + [InlineData("ru-RU", "1.414*")] public void GridLengthTypeConverter_StarValues_ProducesConsistentOutput_AcrossCultures(string cultureName, string gridLengthValue) { // Set both current and UI cultures to test locale @@ -75,27 +74,29 @@ public TestPage() var (result, generated) = RunGenerator(xaml, code); // Should not have any diagnostics/errors - Assert.IsFalse(result.Diagnostics.Any(d => d.Severity == DiagnosticSeverity.Error), + + Assert.False(result.Diagnostics.Any(d => d.Severity == DiagnosticSeverity.Error), $"Generated code should not have errors. Diagnostics: {string.Join(", ", result.Diagnostics.Select(d => d.ToString()))}"); // The generated code should contain a properly formatted GridLength with period as decimal separator // regardless of the current culture - Assert.IsNotNull(generated, "Generated code should not be null"); + Assert.NotNull(generated); // Extract the numeric value from the input (e.g., "2.5*" -> "2.5") var numericPart = gridLengthValue.Substring(0, gridLengthValue.Length - 1); // The generated code should use period as decimal separator (culture-invariant) // and should contain the GridLength constructor with Star unit type - Assert.That(generated, Does.Contain($"new global::Microsoft.Maui.GridLength({numericPart}, global::Microsoft.Maui.GridUnitType.Star)"), + Assert.True(generated!.Contains($"new global::Microsoft.Maui.GridLength({numericPart}, global::Microsoft.Maui.GridUnitType.Star)", StringComparison.Ordinal), $"Generated code should contain culture-invariant GridLength with value {numericPart}. Generated code: {generated}"); } - [TestCase("en-US", "100.5")] - [TestCase("fr-FR", "200.25")] - [TestCase("de-DE", "50.75")] - [TestCase("es-ES", "150.125")] - [TestCase("ru-RU", "75.875")] - public void GridLengthTypeConverter_AbsoluteValues_ProducesConsistentOutput_AcrossCultures(string cultureName, string gridLengthValue) + [Theory] + [InlineData("en-US", "100.5")] + [InlineData("fr-FR", "200.25")] + [InlineData("de-DE", "50.75")] + [InlineData("es-ES", "150.125")] + [InlineData("ru-RU", "75.875")] + public void GridLengthTypeConverter_AbsoluteValues_ProducesConsistentOutput_AcrossCultures(string cultureName, string gridLengthValue) { System.Threading.Thread.CurrentThread.CurrentCulture = CultureInfo.GetCultureInfo(cultureName); System.Threading.Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo(cultureName); @@ -130,22 +131,23 @@ public TestPage() var (result, generated) = RunGenerator(xaml, code); - Assert.IsFalse(result.Diagnostics.Any(d => d.Severity == DiagnosticSeverity.Error), + Assert.False(result.Diagnostics.Any(d => d.Severity == DiagnosticSeverity.Error), $"Generated code should not have errors. Diagnostics: {string.Join(", ", result.Diagnostics.Select(d => d.ToString()))}"); - Assert.IsNotNull(generated, "Generated code should not be null"); + Assert.NotNull(generated); // The generated code should use period as decimal separator and Absolute unit type - Assert.That(generated, Does.Contain($"new global::Microsoft.Maui.GridLength({gridLengthValue}, global::Microsoft.Maui.GridUnitType.Absolute)"), + Assert.True(generated!.Contains($"new global::Microsoft.Maui.GridLength({gridLengthValue}, global::Microsoft.Maui.GridUnitType.Absolute)", StringComparison.Ordinal), $"Generated code should contain culture-invariant GridLength with absolute value {gridLengthValue}. Generated code: {generated}"); } - [TestCase("en-US")] - [TestCase("fr-FR")] - [TestCase("de-DE")] - [TestCase("es-ES")] - [TestCase("ru-RU")] - public void GridLengthTypeConverter_SpecialValues_ProducesConsistentOutput_AcrossCultures(string cultureName) + [Theory] + [InlineData("en-US")] + [InlineData("fr-FR")] + [InlineData("de-DE")] + [InlineData("es-ES")] + [InlineData("ru-RU")] + public void GridLengthTypeConverter_SpecialValues_ProducesConsistentOutput_AcrossCultures(string cultureName) { System.Threading.Thread.CurrentThread.CurrentCulture = CultureInfo.GetCultureInfo(cultureName); System.Threading.Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo(cultureName); @@ -185,19 +187,19 @@ public TestPage() var (result, generated) = RunGenerator(xaml, code); - Assert.IsFalse(result.Diagnostics.Any(d => d.Severity == DiagnosticSeverity.Error), + Assert.False(result.Diagnostics.Any(d => d.Severity == DiagnosticSeverity.Error), $"Generated code should not have errors. Diagnostics: {string.Join(", ", result.Diagnostics.Select(d => d.ToString()))}"); - Assert.IsNotNull(generated, "Generated code should not be null"); + Assert.NotNull(generated); // Check for special values that should be culture-independent - Assert.That(generated, Does.Contain("global::Microsoft.Maui.GridLength.Star"), + Assert.True(generated!.Contains("global::Microsoft.Maui.GridLength.Star", StringComparison.Ordinal), "Generated code should contain GridLength.Star for '*' values"); - Assert.That(generated, Does.Contain("global::Microsoft.Maui.GridLength.Auto"), + Assert.True(generated.Contains("global::Microsoft.Maui.GridLength.Auto", StringComparison.Ordinal), "Generated code should contain GridLength.Auto for 'Auto' values"); } - [Test] + [Fact] public void GridLengthTypeConverter_StarValue_GeneratesGridLengthStar() { const string xaml = """ @@ -233,17 +235,17 @@ public TestPage() var (result, generated) = RunGenerator(xaml, code); - Assert.IsFalse(result.Diagnostics.Any(d => d.Severity == DiagnosticSeverity.Error), + Assert.False(result.Diagnostics.Any(d => d.Severity == DiagnosticSeverity.Error), $"Generated code should not have errors. Diagnostics: {string.Join(", ", result.Diagnostics.Select(d => d.ToString()))}"); - Assert.IsNotNull(generated, "Generated code should not be null"); + Assert.NotNull(generated); // Should generate GridLength.Star for "*" value - Assert.That(generated, Does.Contain("global::Microsoft.Maui.GridLength.Star"), + Assert.True(generated!.Contains("global::Microsoft.Maui.GridLength.Star", StringComparison.Ordinal), "Generated code should contain GridLength.Star for '*' value"); } - [Test] + [Fact] public void GridLengthTypeConverter_AutoValue_GeneratesGridLengthAuto() { const string xaml = """ @@ -279,17 +281,17 @@ public TestPage() var (result, generated) = RunGenerator(xaml, code); - Assert.IsFalse(result.Diagnostics.Any(d => d.Severity == DiagnosticSeverity.Error), + Assert.False(result.Diagnostics.Any(d => d.Severity == DiagnosticSeverity.Error), $"Generated code should not have errors. Diagnostics: {string.Join(", ", result.Diagnostics.Select(d => d.ToString()))}"); - Assert.IsNotNull(generated, "Generated code should not be null"); + Assert.NotNull(generated); // Should generate GridLength.Auto for "Auto" value - Assert.That(generated, Does.Contain("global::Microsoft.Maui.GridLength.Auto"), + Assert.True(generated!.Contains("global::Microsoft.Maui.GridLength.Auto", StringComparison.Ordinal), "Generated code should contain GridLength.Auto for 'Auto' value"); } - [Test] + [Fact] public void EnumTypeConverter() { const string xaml = """ @@ -321,13 +323,13 @@ public TestPage() var (result, generated) = RunGenerator(xaml, code); - Assert.IsFalse(result.Diagnostics.Any(d => d.Severity == DiagnosticSeverity.Error), + Assert.False(result.Diagnostics.Any(d => d.Severity == DiagnosticSeverity.Error), $"Generated code should not have errors. Diagnostics: {string.Join(", ", result.Diagnostics.Select(d => d.ToString()))}"); - Assert.IsNotNull(generated, "Generated code should not be null"); + Assert.NotNull(generated); // Should generate FlexDirection.Row for "Row" value - Assert.That(generated, Does.Contain("flexLayout.SetValue(global::Microsoft.Maui.Controls.FlexLayout.DirectionProperty, global::Microsoft.Maui.Layouts.FlexDirection.Row);"), + Assert.True(generated!.Contains("flexLayout.SetValue(global::Microsoft.Maui.Controls.FlexLayout.DirectionProperty, global::Microsoft.Maui.Layouts.FlexDirection.Row);", StringComparison.Ordinal), "Generated code should contain FlexDirection.Row for 'Row' value"); } } diff --git a/src/Controls/tests/SourceGen.UnitTests/NamingHelpersTests.cs b/src/Controls/tests/SourceGen.UnitTests/NamingHelpersTests.cs index 0a1496dedd50..50a5b5693901 100644 --- a/src/Controls/tests/SourceGen.UnitTests/NamingHelpersTests.cs +++ b/src/Controls/tests/SourceGen.UnitTests/NamingHelpersTests.cs @@ -1,6 +1,6 @@ using Microsoft.CodeAnalysis.CSharp; using Microsoft.Maui.Controls.Xaml.UnitTests.SourceGen; -using NUnit.Framework; +using Xunit; namespace Microsoft.Maui.Controls.SourceGen.UnitTests; @@ -26,20 +26,20 @@ public class TestClass """; - [Test] + [Fact] public void CreateVariableName() { var compilation = SourceGeneratorDriver.CreateMauiCompilation(); compilation = compilation.AddSyntaxTrees(CSharpSyntaxTree.ParseText(code)); var context = new object(); - Assert.That(NamingHelpers.CreateUniqueVariableNameImpl(context, compilation.GetTypeByMetadataName("Test.TestClass")!), Is.EqualTo("testClass")); - Assert.That(NamingHelpers.CreateUniqueVariableNameImpl(context, compilation.GetTypeByMetadataName("Test.TestClass")!), Is.EqualTo("testClass1")); + Assert.Equal("testClass", NamingHelpers.CreateUniqueVariableNameImpl(context, compilation.GetTypeByMetadataName("Test.TestClass")!)); + Assert.Equal("testClass1", NamingHelpers.CreateUniqueVariableNameImpl(context, compilation.GetTypeByMetadataName("Test.TestClass")!)); - Assert.That(NamingHelpers.CreateUniqueVariableNameImpl(new object(), compilation.GetTypeByMetadataName("Test.TestClass`1")!), Is.EqualTo("testClass")); - Assert.That(NamingHelpers.CreateUniqueVariableNameImpl(new object(), compilation.GetTypeByMetadataName("Test.TestClass`1")!.Construct(compilation.GetTypeByMetadataName("Test.TestClass")!)), Is.EqualTo("testClass")); + Assert.Equal("testClass", NamingHelpers.CreateUniqueVariableNameImpl(new object(), compilation.GetTypeByMetadataName("Test.TestClass`1")!)); + Assert.Equal("testClass", NamingHelpers.CreateUniqueVariableNameImpl(new object(), compilation.GetTypeByMetadataName("Test.TestClass`1")!.Construct(compilation.GetTypeByMetadataName("Test.TestClass")!))); - Assert.That(NamingHelpers.CreateUniqueVariableNameImpl(new object(), compilation.CreateArrayTypeSymbol(compilation.GetTypeByMetadataName("Test.TestClass")!)!), Is.EqualTo("testClassArray")); + Assert.Equal("testClassArray", NamingHelpers.CreateUniqueVariableNameImpl(new object(), compilation.CreateArrayTypeSymbol(compilation.GetTypeByMetadataName("Test.TestClass")!)!)); - Assert.That(NamingHelpers.CreateUniqueVariableNameImpl(new object(), compilation.GetTypeByMetadataName("Test.TestClass+Nested")!), Is.EqualTo("nested")); + Assert.Equal("nested", NamingHelpers.CreateUniqueVariableNameImpl(new object(), compilation.GetTypeByMetadataName("Test.TestClass+Nested")!)); } } \ No newline at end of file diff --git a/src/Controls/tests/SourceGen.UnitTests/NoBaseClassOnCodeBehind.cs b/src/Controls/tests/SourceGen.UnitTests/NoBaseClassOnCodeBehind.cs index fb0e489e9bce..f95b3fd22b4b 100644 --- a/src/Controls/tests/SourceGen.UnitTests/NoBaseClassOnCodeBehind.cs +++ b/src/Controls/tests/SourceGen.UnitTests/NoBaseClassOnCodeBehind.cs @@ -1,12 +1,12 @@ using System; using System.Linq; -using NUnit.Framework; +using Xunit; namespace Microsoft.Maui.Controls.SourceGen.UnitTests; public class NoBaseClassOnCodeBehind : SourceGenXamlInitializeComponentTestBase { - [Test] + [Fact] //as the base class ContentPage is only defined in the sg.cs, //it's not part of the compilation when we generate initializecomponent, and the Resources property can't be found public void CodeBehindWithNoBaseClass() @@ -44,6 +44,6 @@ public TestPage() """; var (result, generated) = RunGenerator(xaml, code); - Assert.IsFalse(result.Diagnostics.Any()); + Assert.False(result.Diagnostics.Any()); } } \ No newline at end of file diff --git a/src/Controls/tests/SourceGen.UnitTests/NullableProperties.cs b/src/Controls/tests/SourceGen.UnitTests/NullableProperties.cs index 960ef0c76bda..c62457706719 100644 --- a/src/Controls/tests/SourceGen.UnitTests/NullableProperties.cs +++ b/src/Controls/tests/SourceGen.UnitTests/NullableProperties.cs @@ -1,13 +1,13 @@ using System; using System.Linq; -using NUnit.Framework; +using Xunit; namespace Microsoft.Maui.Controls.SourceGen.UnitTests; public class NullableProperties : SourceGenXamlInitializeComponentTestBase { - [Test] + [Fact] public void NullableStringPropertyShouldWork() { var xaml = @@ -52,9 +52,9 @@ public class EventToCommandBehavior : Behavior """; var (result, generated) = RunGenerator(xaml, code); - Assert.IsFalse(result.Diagnostics.Any()); + Assert.False(result.Diagnostics.Any()); - Assert.IsTrue(generated?.Contains("Test.EventToCommandBehavior", StringComparison.Ordinal)); - Assert.IsTrue(generated?.Contains("eventToCommandBehavior.SetValue(global::Test.EventToCommandBehavior.EventNameProperty", StringComparison.Ordinal)); + Assert.True(generated?.Contains("Test.EventToCommandBehavior", StringComparison.Ordinal) ?? false); + Assert.True(generated?.Contains("eventToCommandBehavior.SetValue(global::Test.EventToCommandBehavior.EventNameProperty", StringComparison.Ordinal) ?? false); } } \ No newline at end of file diff --git a/src/Controls/tests/SourceGen.UnitTests/SourceGen.UnitTests.csproj b/src/Controls/tests/SourceGen.UnitTests/SourceGen.UnitTests.csproj index 3dff5a2aa1d0..ad24995ffe37 100644 --- a/src/Controls/tests/SourceGen.UnitTests/SourceGen.UnitTests.csproj +++ b/src/Controls/tests/SourceGen.UnitTests/SourceGen.UnitTests.csproj @@ -24,8 +24,11 @@ - - + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + diff --git a/src/Controls/tests/SourceGen.UnitTests/SourceGenCssTests.cs b/src/Controls/tests/SourceGen.UnitTests/SourceGenCssTests.cs index 2cffaae46b57..b496dafc567d 100644 --- a/src/Controls/tests/SourceGen.UnitTests/SourceGenCssTests.cs +++ b/src/Controls/tests/SourceGen.UnitTests/SourceGenCssTests.cs @@ -3,7 +3,7 @@ using System.Linq; using Microsoft.CodeAnalysis; using Microsoft.Maui.Controls.SourceGen; -using NUnit.Framework; +using Xunit; using static Microsoft.Maui.Controls.Xaml.UnitTests.SourceGen.SourceGeneratorDriver; @@ -14,7 +14,7 @@ public class SourceGenCssTests : SourceGenTestsBase private record AdditionalCssFile(string Path, string Content, string? RelativePath = null, string? TargetPath = null, string? ManifestResourceName = null, string? TargetFramework = null) : AdditionalFile(Text: SourceGeneratorDriver.ToAdditionalText(Path, Content), Kind: "Css", RelativePath: RelativePath ?? Path, TargetPath: TargetPath, ManifestResourceName: ManifestResourceName ?? Path, TargetFramework: TargetFramework, NoWarn: ""); - [Test] + [Fact] public void TestCodeBehindGenerator_BasicCss() { var css = @@ -28,14 +28,14 @@ public void TestCodeBehindGenerator_BasicCss() var cssFile = new AdditionalCssFile("Test.css", css); var result = SourceGeneratorDriver.RunGenerator(compilation, cssFile); - Assert.IsFalse(result.Diagnostics.Any()); + Assert.False(result.Diagnostics.Any()); var generated = result.Results.Single().GeneratedSources.Single(gs => gs.HintName.EndsWith(".sg.cs", StringComparison.OrdinalIgnoreCase)).SourceText.ToString(); - Assert.IsTrue(generated.Contains($"XamlResourceId(\"{cssFile.ManifestResourceName}\", \"{cssFile.Path}\"", StringComparison.Ordinal)); + Assert.Contains($"XamlResourceId(\"{cssFile.ManifestResourceName}\", \"{cssFile.Path}\"", generated, StringComparison.Ordinal); } - [Test] + [Fact] public void TestCodeBehindGenerator_ModifiedCss() { var css = @@ -61,10 +61,10 @@ public void TestCodeBehindGenerator_ModifiedCss() var output1 = result1.GeneratedSources.Single(gs => gs.HintName.EndsWith(".sg.cs", StringComparison.OrdinalIgnoreCase)).SourceText.ToString(); var output2 = result2.GeneratedSources.Single(gs => gs.HintName.EndsWith(".sg.cs", StringComparison.OrdinalIgnoreCase)).SourceText.ToString(); - // Assert.IsTrue(result1.TrackedSteps.All(s => s.Value.Single().Outputs.Single().Reason == IncrementalStepRunReason.New)); - Assert.AreEqual(output1, output2); + // Assert.True(result1.TrackedSteps.All(s => s.Value.Single().Outputs.Single().Reason == IncrementalStepRunReason.New)); + Assert.Equal(output1, output2); - Assert.IsTrue(output1.Contains($"XamlResourceId(\"{cssFile.ManifestResourceName}\", \"{cssFile.Path}\"", StringComparison.Ordinal)); + Assert.Contains($"XamlResourceId(\"{cssFile.ManifestResourceName}\", \"{cssFile.Path}\"", output1, StringComparison.Ordinal); (GeneratorDriver, Compilation) ApplyChanges(GeneratorDriver driver, Compilation compilation) { diff --git a/src/Controls/tests/SourceGen.UnitTests/SourceGenTestsBase.cs b/src/Controls/tests/SourceGen.UnitTests/SourceGenTestsBase.cs index 7d6f98b41679..31cb265168fc 100644 --- a/src/Controls/tests/SourceGen.UnitTests/SourceGenTestsBase.cs +++ b/src/Controls/tests/SourceGen.UnitTests/SourceGenTestsBase.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using System.Linq; using Microsoft.CodeAnalysis; -using NUnit.Framework; +using Xunit; namespace Microsoft.Maui.Controls.Xaml.UnitTests.SourceGen; @@ -12,7 +12,7 @@ public static void VerifyStepRunReasons(GeneratorRunResult result2, Dictionary(compilation, new AdditionalXamlFile("Test.xaml", xaml)); - Assert.IsFalse(result.Diagnostics.Any()); + Assert.False(result.Diagnostics.Any()); var generated = result.Results.Single().GeneratedSources.Single(gs => gs.HintName.EndsWith(".sg.cs", StringComparison.OrdinalIgnoreCase)).SourceText.ToString(); - Assert.IsTrue(generated.Contains("Microsoft.Maui.Controls.Button MyButton", StringComparison.Ordinal)); - Assert.IsTrue(generated.Contains("public partial class TestPage : global::Microsoft.Maui.Controls.ContentPage", StringComparison.Ordinal)); + Assert.True(generated.Contains("Microsoft.Maui.Controls.Button MyButton", StringComparison.Ordinal)); + Assert.True(generated.Contains("public partial class TestPage : global::Microsoft.Maui.Controls.ContentPage", StringComparison.Ordinal)); } - [Test] + [Fact] public void TestCodeBehindGenerator_GlobalNamespace() { var xaml = @@ -54,14 +54,14 @@ public void TestCodeBehindGenerator_GlobalNamespace() compilation = compilation.AddSyntaxTrees(CSharpSyntaxTree.ParseText("[assembly: global::Microsoft.Maui.Controls.Xaml.Internals.AllowImplicitXmlnsDeclaration]")); var result = SourceGeneratorDriver.RunGenerator(compilation, new AdditionalXamlFile("Test.xaml", xaml)); - Assert.IsFalse(result.Diagnostics.Any()); + Assert.False(result.Diagnostics.Any()); var generated = result.Results.Single().GeneratedSources.Single(gs => gs.HintName.EndsWith(".sg.cs", StringComparison.OrdinalIgnoreCase)).SourceText.ToString(); - Assert.IsTrue(generated.Contains("Microsoft.Maui.Controls.Button MyButton", StringComparison.Ordinal)); + Assert.True(generated.Contains("Microsoft.Maui.Controls.Button MyButton", StringComparison.Ordinal)); } - [Test] + [Fact] public void TestCodeBehindGenerator_AggregatedXmlns() { var xaml = @@ -84,15 +84,15 @@ public void TestCodeBehindGenerator_AggregatedXmlns() compilation = compilation.AddSyntaxTrees(CSharpSyntaxTree.ParseText(code)); var result = RunGenerator(compilation, new AdditionalXamlFile("Test.xaml", xaml)); - Assert.IsFalse(result.Diagnostics.Any()); + Assert.False(result.Diagnostics.Any()); var generated = result.Results.Single().GeneratedSources.Single(gs => gs.HintName.EndsWith(".sg.cs")).SourceText.ToString(); - Assert.IsTrue(generated.Contains("Microsoft.Maui.Controls.ContentPage", StringComparison.Ordinal)); - Assert.IsTrue(generated.Contains("global::Microsoft.Maui.Controls.Label label", StringComparison.Ordinal)); + Assert.True(generated.Contains("Microsoft.Maui.Controls.ContentPage", StringComparison.Ordinal)); + Assert.True(generated.Contains("global::Microsoft.Maui.Controls.Label label", StringComparison.Ordinal)); } - [Test] + [Fact] public void TestCodeBehindGenerator_AggregatedXmlnsOnRD() { var xaml = @@ -114,14 +114,17 @@ public void TestCodeBehindGenerator_AggregatedXmlnsOnRD() compilation = compilation.AddSyntaxTrees(CSharpSyntaxTree.ParseText(code)); var result = RunGenerator(compilation, new AdditionalXamlFile("Test.xaml", xaml)); - Assert.IsFalse(result.Diagnostics.Any()); + Assert.False(result.Diagnostics.Any()); var generated = result.Results.Single().GeneratedSources.Single(gs => gs.HintName.EndsWith(".sg.cs")).SourceText.ToString(); - Assert.IsTrue(generated.Contains("public partial class __Type", StringComparison.Ordinal)); + Assert.True(generated.Contains("public partial class __Type", StringComparison.Ordinal)); } - public void TestCodeBehindGenerator_LocalXaml([Values] bool resolvedType) + [Theory] + [InlineData(true)] + [InlineData(false)] + public void TestCodeBehindGenerator_LocalXaml(bool resolvedType) { var xaml = """ @@ -165,19 +168,19 @@ public class TestControl : ContentView if (resolvedType) { - Assert.IsFalse(result.Diagnostics.Any()); + Assert.False(result.Diagnostics.Any()); var generated = result.Results.Single().GeneratedSources.Single(gs => gs.HintName.EndsWith(".sg.cs", StringComparison.OrdinalIgnoreCase)).SourceText.ToString(); - Assert.IsTrue(generated.Contains("Test.TestControl MyTestControl", StringComparison.Ordinal)); + Assert.True(generated.Contains("Test.TestControl MyTestControl", StringComparison.Ordinal)); } else { - Assert.IsTrue(result.Diagnostics.Any(d => d.Descriptor.Id == "MAUIX2000")); + Assert.True(result.Diagnostics.Any(d => d.Descriptor.Id == "MAUIX2000")); } } - [Test] + [Fact] public void TestCodeBehindGenerator_CompilationClone() { var xaml = @@ -200,7 +203,7 @@ public void TestCodeBehindGenerator_CompilationClone() var output2 = result2.GeneratedSources.Single(gs => gs.HintName.EndsWith(".sg.cs", StringComparison.OrdinalIgnoreCase)).SourceText.ToString(); // Assert.IsTrue(result1.TrackedSteps.All(s => s.Value.Single().Outputs.Single().Reason == IncrementalStepRunReason.New)); - Assert.AreEqual(output1, output2); + Assert.Equal(output1, output2); (GeneratorDriver, Compilation) ApplyChanges(GeneratorDriver driver, Compilation compilation) { @@ -220,7 +223,7 @@ public void TestCodeBehindGenerator_CompilationClone() VerifyStepRunReasons(result2, expectedReasons); } - [Test] + [Fact] public void TestCodeBehindGenerator_ReferenceAdded() { var xaml = @@ -243,7 +246,7 @@ public void TestCodeBehindGenerator_ReferenceAdded() var output2 = result2.GeneratedSources.Single(gs => gs.HintName.EndsWith(".sg.cs", StringComparison.OrdinalIgnoreCase)).SourceText.ToString(); // Assert.IsTrue(result1.TrackedSteps.All(s => s.Value.Single().Outputs.Single().Reason == IncrementalStepRunReason.New)); - Assert.AreEqual(output1, output2); + Assert.Equal(output1, output2); (GeneratorDriver, Compilation) ApplyChanges(GeneratorDriver driver, Compilation compilation) { @@ -263,7 +266,7 @@ public void TestCodeBehindGenerator_ReferenceAdded() VerifyStepRunReasons(result2, expectedReasons); } - [Test] + [Fact] public void TestCodeBehindGenerator_ModifiedXaml() { var xaml = @@ -297,12 +300,12 @@ public void TestCodeBehindGenerator_ModifiedXaml() var output2 = result2.GeneratedSources.Single(gs => gs.HintName.EndsWith(".sg.cs", StringComparison.OrdinalIgnoreCase)).SourceText.ToString(); // Assert.IsTrue(result1.TrackedSteps.All(s => s.Value.Single().Outputs.Single().Reason == IncrementalStepRunReason.New)); - Assert.AreNotEqual(output1, output2); + Assert.NotEqual(output1, output2); - Assert.IsTrue(output1.Contains("MyButton", StringComparison.Ordinal)); - Assert.IsFalse(output1.Contains("MyButton2", StringComparison.Ordinal)); - Assert.IsTrue(output2.Contains("MyButton", StringComparison.Ordinal)); - Assert.IsTrue(output2.Contains("MyButton2", StringComparison.Ordinal)); + Assert.True(output1.Contains("MyButton", StringComparison.Ordinal)); + Assert.False(output1.Contains("MyButton2", StringComparison.Ordinal)); + Assert.True(output2.Contains("MyButton", StringComparison.Ordinal)); + Assert.True(output2.Contains("MyButton2", StringComparison.Ordinal)); (GeneratorDriver, Compilation) ApplyChanges(GeneratorDriver driver, Compilation compilation) { @@ -324,7 +327,7 @@ public void TestCodeBehindGenerator_ModifiedXaml() VerifyStepRunReasons(result2, expectedReasons); } - [Test] + [Fact] public void TestCodeBehindGenerator_NotXaml() { var xaml = @@ -338,10 +341,10 @@ public void TestCodeBehindGenerator_NotXaml() var result = SourceGeneratorDriver.RunGenerator(compilation, new AdditionalXamlFile("Test.xaml", xaml)); var generated = result.Results.Single().GeneratedSources.Single(gs => gs.HintName.EndsWith(".sg.cs")).SourceText.ToString(); - Assert.That(result.Diagnostics.Any() || string.IsNullOrWhiteSpace(generated)); + Assert.True(result.Diagnostics.Any() || string.IsNullOrWhiteSpace(generated)); } - [Test] + [Fact] public void TestCodeBehindGenerator_ConflictingNames() { var code = @@ -378,12 +381,12 @@ public class Conflicting : Label { } var result = SourceGeneratorDriver.RunGenerator(compilation, new AdditionalXamlFile("Test.xaml", xaml)); - Assert.IsTrue(result.Diagnostics.Any()); + Assert.True(result.Diagnostics.Any()); //var generated = result.Results.Single().GeneratedSources.Single().SourceText.ToString(); } - [Test] + [Fact] public void TestCodeBehindGenerator_DuplicateNames() { var xaml = """ @@ -472,14 +475,14 @@ internal class InternalWithSuffix : Button { } var result = SourceGeneratorDriver.RunGenerator(compilation, new AdditionalXamlFile("Test.xaml", xaml)); - Assert.IsFalse(result.Diagnostics.Any()); + Assert.False(result.Diagnostics.Any()); var generated = result.Results.Single().GeneratedSources.Single(gs => gs.HintName.EndsWith(".sg.cs")).SourceText.ToString(); - Assert.IsTrue(generated.Contains("External.PublicInExternal publicInExternal", StringComparison.Ordinal)); + Assert.True(generated.Contains("External.PublicInExternal publicInExternal", StringComparison.Ordinal)); } - [Test] + [Fact] public void TestCodeBehindGenerator_InternalsVisibleTo() { var xaml = """ @@ -538,10 +541,10 @@ internal class InternalButVisible : Label { } var result = SourceGeneratorDriver.RunGenerator(compilation, new AdditionalXamlFile("Test.xaml", xaml)); - Assert.IsFalse(result.Diagnostics.Any()); + Assert.False(result.Diagnostics.Any()); var generated = result.Results.Single().GeneratedSources.Single(gs => gs.HintName.EndsWith(".sg.cs")).SourceText.ToString(); - Assert.IsTrue(generated.Contains("External.InternalButVisible internalButVisible", StringComparison.Ordinal)); + Assert.True(generated.Contains("External.InternalButVisible internalButVisible", StringComparison.Ordinal)); } } diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/CollectionViewSelectionModeOnDarkTheme.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/CollectionViewSelectionModeOnDarkTheme.png new file mode 100644 index 000000000000..78c264d7b70e Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/CollectionViewSelectionModeOnDarkTheme.png differ diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/CollectionViewSelectionModeOnLightTheme.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/CollectionViewSelectionModeOnLightTheme.png new file mode 100644 index 000000000000..6cc55517b404 Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/CollectionViewSelectionModeOnLightTheme.png differ diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/EntryAndEditorPlaceholderTextColorAppThemeBindingUpdatesOnDarkTheme.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/EntryAndEditorPlaceholderTextColorAppThemeBindingUpdatesOnDarkTheme.png new file mode 100644 index 000000000000..742566c1a0d5 Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/EntryAndEditorPlaceholderTextColorAppThemeBindingUpdatesOnDarkTheme.png differ diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/EntryAndEditorPlaceholderTextColorAppThemeBindingUpdatesOnLightTheme.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/EntryAndEditorPlaceholderTextColorAppThemeBindingUpdatesOnLightTheme.png new file mode 100644 index 000000000000..62d224ce5055 Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/EntryAndEditorPlaceholderTextColorAppThemeBindingUpdatesOnLightTheme.png differ diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/EntryAndEditorTextColorAppThemeBindingUpdatesOnDarkTheme.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/EntryAndEditorTextColorAppThemeBindingUpdatesOnDarkTheme.png new file mode 100644 index 000000000000..386b8b0bc2a4 Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/EntryAndEditorTextColorAppThemeBindingUpdatesOnDarkTheme.png differ diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/EntryAndEditorTextColorAppThemeBindingUpdatesOnLightTheme.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/EntryAndEditorTextColorAppThemeBindingUpdatesOnLightTheme.png new file mode 100644 index 000000000000..1f94e347f83f Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/EntryAndEditorTextColorAppThemeBindingUpdatesOnLightTheme.png differ diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue12134.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue12134.cs index 4a27ffcc3468..afdc150bb9de 100644 --- a/src/Controls/tests/TestCases.HostApp/Issues/Issue12134.cs +++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue12134.cs @@ -85,7 +85,7 @@ private WebView GetWebView() SetCookieContainer(anotherWebView); anotherWebView.Navigated += WebViewOnNavigated; - anotherWebView.Source = "https://dotnet.microsoft.com/apps/xamarin"; + anotherWebView.Source = "https://dotnet.microsoft.com"; return anotherWebView; } diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue18443.xaml b/src/Controls/tests/TestCases.HostApp/Issues/Issue18443.xaml index 51fb6eaf19c7..2aefca703aa4 100644 --- a/src/Controls/tests/TestCases.HostApp/Issues/Issue18443.xaml +++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue18443.xaml @@ -4,6 +4,6 @@ x:Class="Maui.Controls.Sample.Issues.Issue18443" SafeAreaEdges="Container"> - + \ No newline at end of file diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue30868.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue30868.cs new file mode 100644 index 000000000000..cb6ae9902230 --- /dev/null +++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue30868.cs @@ -0,0 +1,46 @@ +using System.Collections.ObjectModel; + +namespace Maui.Controls.Sample.Issues; + +[Issue(IssueTracker.Github, 30868, "CollectionView selection visual states", PlatformAffected.UWP)] +public class Issue30868 : ContentPage +{ + public ObservableCollection Items { get; set; } + + public Issue30868() + { + Items = new ObservableCollection + { + "Item 1", + "Item 2", + "Item 3" + }; + + var collectionView = new CollectionView + { + ItemsSource = Items, + SelectionMode = SelectionMode.Multiple, + HeightRequest = 40, + AutomationId = "collectionViewSelectionMode", + ItemsLayout = new LinearItemsLayout(ItemsLayoutOrientation.Horizontal) + { + ItemSpacing = 4 + }, + ItemTemplate = new DataTemplate(() => + { + var label = new Label + { + FontSize = 16, + VerticalOptions = LayoutOptions.Center, + VerticalTextAlignment = TextAlignment.Center, + Padding = new Thickness(10, 0, 10, 10), + TextColor = Colors.Purple + }; + label.SetBinding(Label.TextProperty, "."); + return label; + }) + }; + + Content = collectionView; + } +} \ No newline at end of file diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue31372.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue31372.cs new file mode 100644 index 000000000000..200d0da29507 --- /dev/null +++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue31372.cs @@ -0,0 +1,59 @@ +namespace Maui.Controls.Sample.Issues; + +[Issue(IssueTracker.Github, 31372, "IsPresented=true Not Working on Initial Value in FlyoutPage", PlatformAffected.UWP | PlatformAffected.macOS)] + +public class Issue31372 : NavigationPage +{ + public Issue31372() + { + PushAsync(new Issue31372Page()); + } +} + +public class Issue31372Page : FlyoutPage +{ + public Issue31372Page() + { + + // Create Flyout Page + var flyoutPage = new ContentPage + { + Title = "Flyout", + Content = new StackLayout + { + Children = + { + new Label + { + Text = "This is Flyout", + AutomationId = "FlyoutLabel" + } + } + } + }; + + // Create Detail Page + var detailPage = new ContentPage + { + Title = "Detailpage", + Content = new StackLayout + { + Padding = 16, + Spacing = 16, + Children = + { + new Label + { + Text = "This is Detail Page which displays additional information." + }, + } + } + }; + + // Assign Flyout and Detail + Flyout = flyoutPage; + Detail = new NavigationPage(detailPage); + FlyoutLayoutBehavior = FlyoutLayoutBehavior.Popover; + IsPresented = true; + } +} \ No newline at end of file diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue31731.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue31731.cs new file mode 100644 index 000000000000..6a2100d681dd --- /dev/null +++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue31731.cs @@ -0,0 +1,89 @@ +namespace Maui.Controls.Sample.Issues +{ + [Issue(IssueTracker.Github, 31731, "Picker dialog causes crash when page is popped while dialog is open", PlatformAffected.Android)] + public class Issue31731 : NavigationPage + { + public Issue31731() : base(new MainPage()) + { + } + + public class MainPage : ContentPage + { + public MainPage() + { + var button = new Button + { + Text = "Navigate to Picker Page", + AutomationId = "navigateButton" + }; + + button.Clicked += OnNavigateClicked; + + var statusLabel = new Label + { + Text = "Status: Ready", + AutomationId = "statusLabel" + }; + + Content = new StackLayout + { + Padding = new Thickness(20), + Children = { statusLabel, button } + }; + } + + private void OnNavigateClicked(object sender, EventArgs e) + { + Navigation.PushAsync(new PickerPage()); + } + } + + public class PickerPage : ContentPage + { + public PickerPage() + { + var picker = new Picker + { + Title = "Select a color", + ItemsSource = new List { "Red", "Green", "Blue", "Yellow", "Purple" }, + AutomationId = "colorPicker" + }; + + var instructionsLabel = new Label + { + Text = "Tap the picker to open the dialog, then wait for auto navigation back (3 seconds). The app should not crash.", + AutomationId = "instructionsLabel", + Margin = new Thickness(0, 0, 0, 20) + }; + + var statusLabel = new Label + { + Text = "Status: Page loaded", + AutomationId = "pageStatusLabel" + }; + + Content = new StackLayout + { + Padding = new Thickness(20), + Children = { instructionsLabel, statusLabel, picker } + }; + } + + protected override void OnNavigatedTo(NavigatedToEventArgs args) + { + base.OnNavigatedTo(args); + + // Simulate the scenario: navigate back after 3 seconds + // This can cause a crash if the picker dialog is open + _ = Task.Run(async () => + { + await Task.Delay(3000); + await Dispatcher.DispatchAsync(async () => + { + await Navigation.PopToRootAsync(); + }); + }); + } + } + } +} \ No newline at end of file diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue31889.xaml b/src/Controls/tests/TestCases.HostApp/Issues/Issue31889.xaml new file mode 100644 index 000000000000..529e9216fd18 --- /dev/null +++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue31889.xaml @@ -0,0 +1,46 @@ + + + + + + + + +