diff --git a/.azuredevops/policies/branchClassification.yml b/.azuredevops/policies/branchClassification.yml index af67a75b1138..59b5c90e76e7 100644 --- a/.azuredevops/policies/branchClassification.yml +++ b/.azuredevops/policies/branchClassification.yml @@ -1,5 +1,6 @@ description: Branch classification configuration for repository resource: repository +disabled: false configuration: branchClassificationSettings: defaultClassification: nonproduction diff --git a/eng/pipelines/common/ui-tests-collect-snapshot-diffs.yml b/eng/pipelines/common/ui-tests-collect-snapshot-diffs.yml new file mode 100644 index 000000000000..f25e2b240db6 --- /dev/null +++ b/eng/pipelines/common/ui-tests-collect-snapshot-diffs.yml @@ -0,0 +1,36 @@ +parameters: + platform: '' # The platform name for logging (e.g., 'Android', 'iOS', 'Windows', 'Mac') + artifactName: '' # The artifact name to publish (e.g., 'uitest-snapshot-results-android') + +steps: +- task: PowerShell@2 + condition: always() + displayName: Check for ${{ parameters.platform }} snapshot diffs + name: Check${{ replace(parameters.platform, ' ', '') }}Snapshots # NOTE: This step name must match the variable reference in the condition below + inputs: + targetType: 'inline' + script: | + $snapshotDiffPath = "$(Build.ArtifactStagingDirectory)/Controls.TestCases.Shared.Tests/snapshots-diff" + Write-Host "Checking for ${{ parameters.platform }} snapshot diffs at: $snapshotDiffPath" + + if (Test-Path $snapshotDiffPath) { + $diffFiles = Get-ChildItem $snapshotDiffPath -File -Recurse + + if ($diffFiles.Count -gt 0) { + Write-Host "✅ Found $($diffFiles.Count) ${{ parameters.platform }} snapshot diff files in snapshots-diff directory" + Write-Host "##vso[task.setvariable variable=snapshotsExist;isOutput=true]true" + } else { + Write-Host "â„šī¸ snapshots-diff directory exists but no ${{ parameters.platform }} diff files found" + Write-Host "##vso[task.setvariable variable=snapshotsExist;isOutput=true]false" + } + } else { + Write-Host "❌ No ${{ parameters.platform }} snapshots-diff directory found at: $snapshotDiffPath" + Write-Host "##vso[task.setvariable variable=snapshotsExist;isOutput=true]false" + } + +- task: PublishPipelineArtifact@1 + condition: eq(variables['Check${{ replace(parameters.platform, ' ', '') }}Snapshots.snapshotsExist'], 'true') # NOTE: This variable reference must match the step name above + displayName: Publish ${{ parameters.platform }} snapshot diffs + inputs: + targetPath: $(Build.ArtifactStagingDirectory)/Controls.TestCases.Shared.Tests + artifact: ${{ parameters.artifactName }} \ No newline at end of file diff --git a/eng/pipelines/common/ui-tests-steps.yml b/eng/pipelines/common/ui-tests-steps.yml index 4c0d1d849d0e..a82ba3cdf881 100644 --- a/eng/pipelines/common/ui-tests-steps.yml +++ b/eng/pipelines/common/ui-tests-steps.yml @@ -175,6 +175,7 @@ steps: condition: always() displayName: publish artifacts +# Enable Notification Center re-enabled only for catalyst - ${{ if eq(parameters.platform, 'catalyst')}}: - bash: | chmod +x $(System.DefaultWorkingDirectory)/eng/scripts/enable-notification-center.sh @@ -182,83 +183,3 @@ steps: displayName: 'Enable Notification Center' continueOnError: true timeoutInMinutes: 60 - - - ${{ if eq(parameters.platform, 'android')}}: - - task: PowerShell@2 - inputs: - targetType: 'inline' - script: | - $folderPath = "${{ parameters.checkoutDirectory }}/artifacts/bin/Controls.TestCases.Android.Tests/${{ parameters.configuration }}/net9.0/snapshots-diff" - if (Test-Path $folderPath) { - Write-Host "##vso[task.setvariable variable=androidFolderExists;isOutput=true]true" - } else { - Write-Host "##vso[task.setvariable variable=androidFolderExists;isOutput=true]false" - } - name: CheckAndroidFolderExists - - - task: PublishBuildArtifacts@1 - condition: eq(variables['CheckAndroidFolderExists.androidFolderExists'], 'true') - displayName: publish Android screenshots artifacts - inputs: - PathToPublish: ${{ parameters.checkoutDirectory }}/artifacts/bin/Controls.TestCases.Android.Tests/${{ parameters.configuration }}/net9.0/snapshots-diff - ArtifactName: uitest-snapshot-results-android - - - ${{ if eq(parameters.platform, 'ios')}}: - - task: PowerShell@2 - inputs: - targetType: 'inline' - script: | - $folderPath = "${{ parameters.checkoutDirectory }}/artifacts/bin/Controls.TestCases.iOS.Tests/${{ parameters.configuration }}/net9.0/snapshots-diff" - if (Test-Path $folderPath) { - Write-Host "##vso[task.setvariable variable=iosFolderExists;isOutput=true]true" - } else { - Write-Host "##vso[task.setvariable variable=iosFolderExists;isOutput=true]false" - } - name: CheckIosFolderExists - - - task: PublishBuildArtifacts@1 - condition: eq(variables['CheckIosFolderExists.iosFolderExists'], 'true') - displayName: publish iOS screenshots artifacts - inputs: - PathToPublish: ${{ parameters.checkoutDirectory }}/artifacts/bin/Controls.TestCases.iOS.Tests/${{ parameters.configuration }}/net9.0/snapshots-diff - ArtifactName: uitest-snapshot-results-ios - - - ${{ if eq(parameters.platform, 'windows')}}: - - task: PowerShell@2 - inputs: - targetType: 'inline' - script: | - $folderPath = "${{ parameters.checkoutDirectory }}/artifacts/bin/Controls.TestCases.WinUI.Tests/${{ parameters.configuration }}/net9.0/snapshots-diff" - if (Test-Path $folderPath) { - Write-Host "##vso[task.setvariable variable=windowsFolderExists;isOutput=true]true" - } else { - Write-Host "##vso[task.setvariable variable=windowsFolderExists;isOutput=true]false" - } - name: CheckWindowsFolderExists - - - task: PublishBuildArtifacts@1 - condition: eq(variables['CheckWindowsFolderExists.windowsFolderExists'], 'true') - displayName: publish Windows screenshots artifacts - inputs: - PathToPublish: ${{ parameters.checkoutDirectory }}/artifacts/bin/Controls.TestCases.WinUI.Tests/${{ parameters.configuration }}/net9.0/snapshots-diff - ArtifactName: uitest-snapshot-results-windows - - - ${{ if eq(parameters.platform, 'catalyst')}}: - - task: PowerShell@2 - inputs: - targetType: 'inline' - script: | - $folderPath = "${{ parameters.checkoutDirectory }}/artifacts/bin/Controls.TestCases.Mac.Tests/${{ parameters.configuration }}/net9.0/snapshots-diff" - if (Test-Path $folderPath) { - Write-Host "##vso[task.setvariable variable=macFolderExists;isOutput=true]true" - } else { - Write-Host "##vso[task.setvariable variable=macFolderExists;isOutput=true]false" - } - name: CheckMacFolderExists - - - task: PublishBuildArtifacts@1 - condition: eq(variables['CheckMacFolderExists.macFolderExists'], 'true') - displayName: publish Mac screenshots artifacts - inputs: - PathToPublish: ${{ parameters.checkoutDirectory }}/artifacts/bin/Controls.TestCases.Mac.Tests/${{ parameters.configuration }}/net9.0/snapshots-diff - ArtifactName: uitest-snapshot-results-mac diff --git a/eng/pipelines/common/ui-tests.yml b/eng/pipelines/common/ui-tests.yml index 601cbdab22a1..2cb004e662eb 100644 --- a/eng/pipelines/common/ui-tests.yml +++ b/eng/pipelines/common/ui-tests.yml @@ -147,7 +147,20 @@ stages: provisionatorChannel: ${{ parameters.provisionatorChannel }} testFilter: $(CATEGORYGROUP) skipProvisioning: ${{ parameters.skipProvisioning }} - + + # Collect and publish Android snapshot diffs + - template: ui-tests-collect-snapshot-diffs.yml + parameters: + platform: 'Android' + artifactName: 'uitest-snapshot-results-android-$(System.JobName)-$(System.JobAttempt)' + + # Collect and publish Android snapshot diffs + - template: ui-tests-collect-snapshot-diffs.yml + parameters: + platform: 'Android' + artifactName: 'uitest-snapshot-results-android-$(System.JobName)-$(System.JobAttempt)' + + - stage: android_ui_tests_coreclr displayName: Android UITests CoreClr dependsOn: build_ui_tests_coreclr @@ -186,6 +199,12 @@ stages: testFilter: $(CATEGORYGROUP) skipProvisioning: ${{ parameters.skipProvisioning }} runtimeVariant: "CoreCLR" + + # Collect and publish Android snapshot diffs + - template: ui-tests-collect-snapshot-diffs.yml + parameters: + platform: 'Android' + artifactName: 'uitest-snapshot-results-android-$(System.JobName)-$(System.JobAttempt)' - stage: ios_ui_tests_mono displayName: iOS UITests Mono @@ -227,6 +246,12 @@ stages: runtimeVariant : "Mono" testFilter: $(CATEGORYGROUP) skipProvisioning: ${{ parameters.skipProvisioning }} + + # Collect and publish iOS snapshot diffs + - template: ui-tests-collect-snapshot-diffs.yml + parameters: + platform: 'iOS' + artifactName: 'uitest-snapshot-results-ios-$(System.JobName)-$(System.JobAttempt)' - stage: ios_ui_tests_mono_cv2 displayName: iOS UITests Mono CollectionView2 @@ -264,6 +289,12 @@ stages: testFilter: "CollectionView" testConfigurationArgs: "CollectionView2" skipProvisioning: ${{ parameters.skipProvisioning }} + + # Collect and publish iOS CV2 snapshot diffs + - template: ui-tests-collect-snapshot-diffs.yml + parameters: + platform: 'iOS CV2' + artifactName: 'uitest-snapshot-results-ios-cv2-$(System.JobName)-$(System.JobAttempt)' - stage: ios_ui_tests_mono_carv2 displayName: iOS UITests Mono CarouselView2 @@ -301,6 +332,12 @@ stages: testFilter: "CarouselView" testConfigurationArgs: "CollectionView2" skipProvisioning: ${{ parameters.skipProvisioning }} + + # Collect and publish iOS CARV2 snapshot diffs + - template: ui-tests-collect-snapshot-diffs.yml + parameters: + platform: 'iOS CARV2' + artifactName: 'uitest-snapshot-results-ios-carv2-$(System.JobName)-$(System.JobAttempt)' # NativeAOT iOS UI tests stage - only run when both BuildNativeAOT and RunNativeAOT parameters are true - ${{ if and(eq(parameters.BuildNativeAOT, true), eq(parameters.RunNativeAOT, true)) }}: @@ -375,6 +412,12 @@ stages: provisionatorChannel: ${{ parameters.provisionatorChannel }} testFilter: $(CATEGORYGROUP) skipProvisioning: ${{ parameters.skipProvisioning }} + + # Collect and publish Windows snapshot diffs + - template: ui-tests-collect-snapshot-diffs.yml + parameters: + platform: 'Windows' + artifactName: 'uitest-snapshot-results-windows-$(System.JobName)-$(System.JobAttempt)' - stage: mac_ui_tests displayName: macOS UITests @@ -407,3 +450,9 @@ stages: provisionatorChannel: ${{ parameters.provisionatorChannel }} testFilter: $(CATEGORYGROUP) skipProvisioning: ${{ parameters.skipProvisioning }} + + # Collect and publish Mac snapshot diffs + - template: ui-tests-collect-snapshot-diffs.yml + parameters: + platform: 'Mac' + artifactName: 'uitest-snapshot-results-mac-$(System.JobName)-$(System.JobAttempt)' diff --git a/src/Controls/src/Core/RadioButton/RadioButton.cs b/src/Controls/src/Core/RadioButton/RadioButton.cs index eea15dbdf01c..6feaad9381e5 100644 --- a/src/Controls/src/Core/RadioButton/RadioButton.cs +++ b/src/Controls/src/Core/RadioButton/RadioButton.cs @@ -57,7 +57,7 @@ public partial class RadioButton : TemplatedView, IElementConfiguration ((RadioButton)b).OnValuePropertyChanged(), - coerceValue: (b, o) => o ?? b); + coerceValue: (b, o) => o ?? b); /// Bindable property for . public static readonly BindableProperty IsCheckedProperty = BindableProperty.Create( diff --git a/src/Controls/tests/Core.UnitTests/RadioButtonTests.cs b/src/Controls/tests/Core.UnitTests/RadioButtonTests.cs index cdb36fd71c16..012499a3dcd4 100644 --- a/src/Controls/tests/Core.UnitTests/RadioButtonTests.cs +++ b/src/Controls/tests/Core.UnitTests/RadioButtonTests.cs @@ -308,7 +308,7 @@ public void GroupNullSelectionClearsAnySelection() public void ValuePropertyCoercedToItselfIfSetToNull() { var radioButton = new RadioButton(); - + Assert.Equal(radioButton, radioButton.Value); radioButton.Value = null; diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue31520.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue31520.cs index 12a1cb73d86e..829d217b57aa 100644 --- a/src/Controls/tests/TestCases.HostApp/Issues/Issue31520.cs +++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue31520.cs @@ -3,121 +3,121 @@ namespace Maui.Controls.Sample.Issues; [Issue(IssueTracker.Github, 31520, "NavigatingFrom is triggered first when using PushAsync", PlatformAffected.Android)] public class Issue31520 : NavigationPage { - public static int count = 0; - public static Label statusLabel; - public static Issue31520Page2 currentPage2; + public static int count = 0; + public static Label statusLabel; + public static Issue31520Page2 currentPage2; - public Issue31520() : base(new Issue31520Page1()) - { + public Issue31520() : base(new Issue31520Page1()) + { - } + } } public class Issue31520Page1 : ContentPage { - public Issue31520Page1() - { - SetupPageContent(); - - Disappearing += (s, e) => - { - Issue31520.count++; - }; - - NavigatingFrom += (s, e) => - { - if (Issue31520.statusLabel is not null) - { - Issue31520.statusLabel.Text = (Issue31520.count == 0) - ? "NavigatingFrom triggered before Disappearing" - : "NavigatingFrom triggered after Disappearing"; - - Issue31520.currentPage2?.UpdateNavigatingStatusLabel(); - } - }; - } - - private void SetupPageContent() - { - Title = "Page 1"; - - Issue31520.statusLabel = new Label - { - Text = "Ready", - AutomationId = "statusLabel" - }; - - var button = new Button - { - Text = "Push", - AutomationId = "PushButton" - }; - button.Clicked += OnCounterClicked; - - Content = new VerticalStackLayout - { - Padding = new Thickness(30, 0), - Spacing = 25, - Children = - { - Issue31520.statusLabel, - button - } - }; - } - - private void OnCounterClicked(object sender, EventArgs e) - { - Navigation.PushAsync(new Issue31520Page2()); - } + public Issue31520Page1() + { + SetupPageContent(); + + Disappearing += (s, e) => + { + Issue31520.count++; + }; + + NavigatingFrom += (s, e) => + { + if (Issue31520.statusLabel is not null) + { + Issue31520.statusLabel.Text = (Issue31520.count == 0) + ? "NavigatingFrom triggered before Disappearing" + : "NavigatingFrom triggered after Disappearing"; + + Issue31520.currentPage2?.UpdateNavigatingStatusLabel(); + } + }; + } + + private void SetupPageContent() + { + Title = "Page 1"; + + Issue31520.statusLabel = new Label + { + Text = "Ready", + AutomationId = "statusLabel" + }; + + var button = new Button + { + Text = "Push", + AutomationId = "PushButton" + }; + button.Clicked += OnCounterClicked; + + Content = new VerticalStackLayout + { + Padding = new Thickness(30, 0), + Spacing = 25, + Children = + { + Issue31520.statusLabel, + button + } + }; + } + + private void OnCounterClicked(object sender, EventArgs e) + { + Navigation.PushAsync(new Issue31520Page2()); + } } public class Issue31520Page2 : ContentPage { - private Label navigatingStatusLabel; - - public Issue31520Page2() - { - Issue31520.currentPage2 = this; - - Appearing += (s, e) => - { - Issue31520.count++; - }; - - Title = "Page 2"; - - var button = new Button - { - Text = "Pop", - AutomationId = "ChildPageButton" - }; - button.Clicked += OnButtonClicked; - - navigatingStatusLabel = new Label - { - Text = Issue31520.statusLabel?.Text ?? "Ready", - AutomationId = "NavigatingStatusLabel" - }; - - Content = new StackLayout - { - Children = - { - navigatingStatusLabel, - button - } - }; - } - - public void UpdateNavigatingStatusLabel() - { - if (navigatingStatusLabel is not null && Issue31520.statusLabel is not null) - navigatingStatusLabel.Text = Issue31520.statusLabel.Text; - } - - private void OnButtonClicked(object sender, EventArgs e) - { - Navigation.PopAsync(); - } + private Label navigatingStatusLabel; + + public Issue31520Page2() + { + Issue31520.currentPage2 = this; + + Appearing += (s, e) => + { + Issue31520.count++; + }; + + Title = "Page 2"; + + var button = new Button + { + Text = "Pop", + AutomationId = "ChildPageButton" + }; + button.Clicked += OnButtonClicked; + + navigatingStatusLabel = new Label + { + Text = Issue31520.statusLabel?.Text ?? "Ready", + AutomationId = "NavigatingStatusLabel" + }; + + Content = new StackLayout + { + Children = + { + navigatingStatusLabel, + button + } + }; + } + + public void UpdateNavigatingStatusLabel() + { + if (navigatingStatusLabel is not null && Issue31520.statusLabel is not null) + navigatingStatusLabel.Text = Issue31520.statusLabel.Text; + } + + private void OnButtonClicked(object sender, EventArgs e) + { + Navigation.PopAsync(); + } } diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue7045.xaml.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue7045.xaml.cs index 3ca7fb8a2712..3e1cfb5d27b7 100644 --- a/src/Controls/tests/TestCases.HostApp/Issues/Issue7045.xaml.cs +++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue7045.xaml.cs @@ -9,7 +9,8 @@ public partial class Issue7045 : Shell public Issue7045() { InitializeComponent(); - NavigateButton.Clicked += async (s, e) => await Current.GoToAsync($"//DetialPage"); ; + NavigateButton.Clicked += async (s, e) => await Current.GoToAsync($"//DetialPage"); + ; DetailPage.BindingContext = this; } } \ No newline at end of file diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue31539.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue31539.cs index 633a798b1ba9..f74a38ba0c91 100644 --- a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue31539.cs +++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue31539.cs @@ -1,4 +1,4 @@ -# if TEST_FAILS_ON_ANDROID && TEST_FAILS_ON_WINDOWS // BackButtonTitle is only applicable for iOS and macOS +#if TEST_FAILS_ON_ANDROID && TEST_FAILS_ON_WINDOWS // BackButtonTitle is only applicable for iOS and macOS using NUnit.Framework; using UITest.Appium; using UITest.Core;