Skip to content
1 change: 1 addition & 0 deletions .azuredevops/policies/branchClassification.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
description: Branch classification configuration for repository
resource: repository
disabled: false
configuration:
branchClassificationSettings:
defaultClassification: nonproduction
Expand Down
36 changes: 36 additions & 0 deletions eng/pipelines/common/ui-tests-collect-snapshot-diffs.yml
Original file line number Diff line number Diff line change
@@ -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
Copy link

Copilot AI Sep 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The step name generation using replace(parameters.platform, ' ', '') creates potential naming conflicts and maintenance issues. Platform values like 'iOS CV2' and 'iOS CARV2' will generate step names 'CheckiOSCV2Snapshots' and 'CheckiOSCARV2Snapshots' which are not intuitive. Consider using a separate parameter for the step identifier or implementing a more robust naming strategy that handles special characters and ensures uniqueness.

Copilot uses AI. Check for mistakes.
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
Copy link

Copilot AI Sep 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The variable reference relies on string manipulation that must exactly match the step name generation logic. This creates a fragile dependency where changes to the platform parameter formatting could break the condition. The duplication of the replace(parameters.platform, ' ', '') logic in two places increases maintenance risk and potential for mismatches.

Copilot uses AI. Check for mistakes.
displayName: Publish ${{ parameters.platform }} snapshot diffs
inputs:
targetPath: $(Build.ArtifactStagingDirectory)/Controls.TestCases.Shared.Tests
artifact: ${{ parameters.artifactName }}
81 changes: 1 addition & 80 deletions eng/pipelines/common/ui-tests-steps.yml
Original file line number Diff line number Diff line change
Expand Up @@ -175,90 +175,11 @@ 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
$(System.DefaultWorkingDirectory)/eng/scripts/enable-notification-center.sh
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
51 changes: 50 additions & 1 deletion eng/pipelines/common/ui-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)) }}:
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)'
2 changes: 1 addition & 1 deletion src/Controls/src/Core/RadioButton/RadioButton.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ public partial class RadioButton : TemplatedView, IElementConfiguration<RadioBut
public static readonly BindableProperty ValueProperty =
BindableProperty.Create(nameof(Value), typeof(object), typeof(RadioButton), null,
propertyChanged: (b, o, n) => ((RadioButton)b).OnValuePropertyChanged(),
coerceValue: (b, o) => o ?? b);
coerceValue: (b, o) => o ?? b);

/// <summary>Bindable property for <see cref="IsChecked"/>.</summary>
public static readonly BindableProperty IsCheckedProperty = BindableProperty.Create(
Expand Down
2 changes: 1 addition & 1 deletion src/Controls/tests/Core.UnitTests/RadioButtonTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -308,7 +308,7 @@ public void GroupNullSelectionClearsAnySelection()
public void ValuePropertyCoercedToItselfIfSetToNull()
{
var radioButton = new RadioButton();

Assert.Equal(radioButton, radioButton.Value);

radioButton.Value = null;
Expand Down
Loading
Loading