diff --git a/eng/cake/dotnet.cake b/eng/cake/dotnet.cake index fe16cca87da4..918774f8fda3 100644 --- a/eng/cake/dotnet.cake +++ b/eng/cake/dotnet.cake @@ -218,6 +218,13 @@ Task("uitests-apphost") properties.Add("_UseNativeAot", "true"); properties.Add("RuntimeIdentifier", "iossimulator-x64"); } + + var useMaterial3 = Argument("usematerial3", false); + if (useMaterial3) + { + Information("Building with Material3 enabled"); + properties.Add("UseMaterial3", "true"); + } if (useNuget) { diff --git a/eng/pipelines/common/ui-tests-build-sample.yml b/eng/pipelines/common/ui-tests-build-sample.yml index 74e64630e60f..bda422092d98 100644 --- a/eng/pipelines/common/ui-tests-build-sample.yml +++ b/eng/pipelines/common/ui-tests-build-sample.yml @@ -11,6 +11,7 @@ parameters: testFilter: '' headless: true runtimeVariant: 'Mono' #Mono, CoreCLR, NativeAOT + useMaterial3: false # NEW PARAMETER - Enable Material3 build steps: - ${{ if eq(parameters.platform, 'ios')}}: @@ -53,9 +54,29 @@ steps: - pwsh: echo "##vso[task.prependpath]$(DotNet.Dir)" displayName: 'Add .NET to PATH' +# Clean artifacts for Material3 build to ensure clean state +# Reference: Controls.TestCases.HostApp.csproj line 17-19 warning comment +- pwsh: | + if (("${{ parameters.useMaterial3 }}" -eq "true") -and ("${{ parameters.platform }}" -eq "android")) { + Write-Host "Cleaning artifacts folder for Material3 clean build" + Remove-Item -Path "$(System.DefaultWorkingDirectory)/artifacts" -Recurse -Force -ErrorAction SilentlyContinue + Write-Host "Artifacts folder cleaned successfully" + } + displayName: 'Clean artifacts for Material3' + - pwsh: | Get-Content $PSCommandPath - ./build.ps1 --target=uitests-apphost --configuration="${{ parameters.configuration }}" --${{ parameters.platform }} --verbosity=diagnostic --usenuget=false --runtimevariant="${{ parameters.runtimeVariant }}" + $buildCommand = "./build.ps1 --target=uitests-apphost --configuration=${{ parameters.configuration }} --${{ parameters.platform }} --verbosity=diagnostic --usenuget=false --runtimevariant=${{ parameters.runtimeVariant }}" + + # Add Material3 build argument if enabled + if (("${{ parameters.useMaterial3 }}" -eq "true") -and ("${{ parameters.platform }}" -eq "android")) { + $buildCommand += " --usematerial3=true" + Write-Host "Building with Material3 enabled" + } elseif ("${{ parameters.useMaterial3 }}" -eq "true") { + Write-Host "Material3 is only supported for Android platform, ignoring useMaterial3 parameter" + } + + Invoke-Expression $buildCommand displayName: 'Build the samples' - bash: | @@ -66,7 +87,7 @@ steps: continueOnError: true - publish: $(System.DefaultWorkingDirectory)/artifacts/bin - condition: and(ne('${{ parameters.platform }}' , 'windows'), ne('${{ parameters.runtimeVariant }}' , 'NativeAOT'), ne('${{ parameters.runtimeVariant }}' , 'CoreCLR'), succeeded()) + condition: and(ne('${{ parameters.platform }}' , 'windows'), ne('${{ parameters.runtimeVariant }}' , 'NativeAOT'), ne('${{ parameters.runtimeVariant }}' , 'CoreCLR'), eq('${{ parameters.useMaterial3 }}', 'false'), succeeded()) artifact: ui-tests-samples - publish: $(System.DefaultWorkingDirectory)/artifacts/bin @@ -74,15 +95,24 @@ steps: artifact: ui-tests-samples-nativeaot - publish: $(System.DefaultWorkingDirectory)/artifacts/bin - condition: and(ne('${{ parameters.platform }}' , 'windows'), eq('${{ parameters.runtimeVariant }}' , 'CoreCLR'), succeeded()) + condition: and(ne('${{ parameters.platform }}' , 'windows'), eq('${{ parameters.runtimeVariant }}' , 'CoreCLR'), eq('${{ parameters.useMaterial3 }}', 'false'), succeeded()) artifact: ui-tests-samples-coreclr +# Material3 artifacts +- publish: $(System.DefaultWorkingDirectory)/artifacts/bin + condition: and(ne('${{ parameters.platform }}' , 'windows'), ne('${{ parameters.runtimeVariant }}' , 'CoreCLR'), eq('${{ parameters.useMaterial3 }}', 'true'), succeeded()) + artifact: ui-tests-samples-material3 + +- publish: $(System.DefaultWorkingDirectory)/artifacts/bin + condition: and(ne('${{ parameters.platform }}' , 'windows'), eq('${{ parameters.runtimeVariant }}' , 'CoreCLR'), eq('${{ parameters.useMaterial3 }}', 'true'), succeeded()) + artifact: ui-tests-samples-material3-coreclr + - publish: $(System.DefaultWorkingDirectory)/artifacts/bin condition: and(eq('${{ parameters.platform }}' , 'windows'), succeeded()) artifact: ui-tests-samples-windows - publish: $(System.DefaultWorkingDirectory)/artifacts/bin - condition: and(ne('${{ parameters.platform }}' , 'windows'), ne('${{ parameters.runtimeVariant }}' , 'NativeAOT'), ne('${{ parameters.runtimeVariant }}' , 'CoreCLR'), failed()) + condition: and(ne('${{ parameters.platform }}' , 'windows'), ne('${{ parameters.runtimeVariant }}' , 'NativeAOT'), ne('${{ parameters.runtimeVariant }}' , 'CoreCLR'), eq('${{ parameters.useMaterial3 }}', 'false'), failed()) artifact: ui-tests-samples_failed_$(System.JobAttempt) - publish: $(System.DefaultWorkingDirectory)/artifacts/bin @@ -90,9 +120,18 @@ steps: artifact: ui-tests-samples-nativeaot_failed_$(System.JobAttempt) - publish: $(System.DefaultWorkingDirectory)/artifacts/bin - condition: and(ne('${{ parameters.platform }}' , 'windows'), eq('${{ parameters.runtimeVariant }}' , 'CoreCLR'), failed()) + condition: and(ne('${{ parameters.platform }}' , 'windows'), eq('${{ parameters.runtimeVariant }}' , 'CoreCLR'), eq('${{ parameters.useMaterial3 }}', 'false'), failed()) artifact: ui-tests-samples-coreclr_failed_$(System.JobAttempt) +# Material3 failure artifacts +- publish: $(System.DefaultWorkingDirectory)/artifacts/bin + condition: and(ne('${{ parameters.platform }}' , 'windows'), ne('${{ parameters.runtimeVariant }}' , 'CoreCLR'), eq('${{ parameters.useMaterial3 }}', 'true'), failed()) + artifact: ui-tests-samples-material3_failed_$(System.JobAttempt) + +- publish: $(System.DefaultWorkingDirectory)/artifacts/bin + condition: and(ne('${{ parameters.platform }}' , 'windows'), eq('${{ parameters.runtimeVariant }}' , 'CoreCLR'), eq('${{ parameters.useMaterial3 }}', 'true'), failed()) + artifact: ui-tests-samples-material3-coreclr_failed_$(System.JobAttempt) + - publish: $(System.DefaultWorkingDirectory)/artifacts/bin condition: and(eq('${{ parameters.platform }}' , 'windows'), failed()) artifact: ui-tests-samples-windows_failed_$(System.JobAttempt) diff --git a/eng/pipelines/common/ui-tests-steps.yml b/eng/pipelines/common/ui-tests-steps.yml index f279e069f783..c172fba9ccd9 100644 --- a/eng/pipelines/common/ui-tests-steps.yml +++ b/eng/pipelines/common/ui-tests-steps.yml @@ -2,12 +2,14 @@ parameters: platform: '' # [ android, ios, windows, catalyst ] path: '' # path to csproj device: '' # the xharness device to use + deviceType: '' # the device type/skin for Android emulator (e.g., pixel_3XL, Nexus 5X) cakeArgs: '' # additional cake args app: '' #path to app to test version: '' #the iOS version' provisionatorChannel: 'latest' configuration: "Release" runtimeVariant: "Mono" + useMaterial3: false # NEW PARAMETER - Enable Material3 build testFilter: '' headless: true testConfigurationArgs: '' @@ -16,17 +18,22 @@ parameters: steps: - task: DownloadPipelineArtifact@2 - condition: and(ne('${{ parameters.platform }}' , 'windows'), eq('${{ parameters.runtimeVariant }}' , 'Mono')) + condition: and(ne('${{ parameters.platform }}' , 'windows'), eq('${{ parameters.runtimeVariant }}' , 'Mono'), eq('${{ parameters.useMaterial3 }}', 'false')) inputs: artifact: ui-tests-samples +- task: DownloadPipelineArtifact@2 + condition: and(ne('${{ parameters.platform }}' , 'windows'), eq('${{ parameters.runtimeVariant }}' , 'Mono'), eq('${{ parameters.useMaterial3 }}', 'true')) + inputs: + artifact: ui-tests-samples-material3 + - task: DownloadPipelineArtifact@2 condition: and(ne('${{ parameters.platform }}' , 'windows'), eq('${{ parameters.runtimeVariant }}' , 'NativeAOT')) inputs: artifact: ui-tests-samples-nativeaot - task: DownloadPipelineArtifact@2 - condition: and(ne('${{ parameters.platform }}' , 'windows'), eq('${{ parameters.runtimeVariant }}' , 'CoreCLR')) + condition: and(ne('${{ parameters.platform }}' , 'windows'), eq('${{ parameters.runtimeVariant }}' , 'CoreCLR'), eq('${{ parameters.useMaterial3 }}', 'false')) inputs: artifact: ui-tests-samples-coreclr @@ -129,6 +136,12 @@ steps: $command += " --runtimevariant=""${{ parameters.runtimeVariant }}""" $command += " --results=""$(TestResultsDirectory)"" --binlog=""$(LogDirectory)"" ${{ parameters.cakeArgs }} --verbosity=diagnostic" + # Add device skin parameter if specified (for Android emulator hardware profile) + $deviceType = "${{ parameters.deviceType }}" + if ($deviceType) { + $command += " --skin=""$deviceType""" + } + $testFilter = "" $testConfigrationArgs = "${{ parameters.testConfigurationArgs }}" diff --git a/eng/pipelines/common/ui-tests.yml b/eng/pipelines/common/ui-tests.yml index efdad1964c0c..4bda02c72734 100644 --- a/eng/pipelines/common/ui-tests.yml +++ b/eng/pipelines/common/ui-tests.yml @@ -6,6 +6,7 @@ parameters: windowsBuildPool: { } macosPool: { } androidApiLevels: [ 30 ] + androidApiLevelsExtended: [ 36 ] # API 36 for Material3 tests with Pixel 3 XL iosVersions: [ 'latest' ] provisionatorChannel: 'latest' timeoutInMinutes: 180 @@ -15,6 +16,7 @@ parameters: categoryGroupsToTest: # Make sure that this list is always up-to-date with src/Controls/tests/TestCases.Shared.Tests/UITestCategories.cs # we might want to improve this somehow depending on how much the categories change over time + # Note: Material3 is intentionally excluded from this list and has its own dedicated build and test stages below with a separate test filter - 'Accessibility,ActionSheet,ActivityIndicator,Animation,AppLinks' - 'Border,BoxView,Brush,Button' - 'CarouselView' @@ -115,6 +117,27 @@ stages: platform: windows skipProvisioning: ${{ parameters.skipProvisioning }} + # Material3 Build Stage - Separate build with UseMaterial3=true (Android-only) + - stage: build_ui_tests_material3 + displayName: Build UITests Material3 Sample App + dependsOn: [] + jobs: + - job: build_ui_tests_material3 + displayName: Build Sample App (Material3) + workspace: + clean: all + pool: ${{ parameters.androidPool }} + variables: + REQUIRED_XCODE: $(DEVICETESTS_REQUIRED_XCODE) + APPIUM_HOME: $(System.DefaultWorkingDirectory)/.appium/ + steps: + - template: ui-tests-build-sample.yml + parameters: + runtimeVariant: "Mono" + platform: android + useMaterial3: true + skipProvisioning: ${{ parameters.skipProvisioning }} + - stage: android_ui_tests displayName: Android UITests dependsOn: build_ui_tests @@ -205,6 +228,46 @@ stages: platform: 'Android' artifactName: 'uitest-snapshot-results-android-$(System.StageName)-$(System.JobName)-$(System.JobAttempt)' + - stage: android_ui_tests_material3 + displayName: Android UITests Material3 + dependsOn: build_ui_tests_material3 + jobs: + - ${{ each project in parameters.projects }}: + - ${{ if ne(project.android, '') }}: + - ${{ each api in parameters.androidApiLevelsExtended }}: + - ${{ if not(containsValue(project.androidApiLevelsExclude, api)) }}: + - job: M3_android_ui_tests_${{ project.name }}_${{ api }} + timeoutInMinutes: 240 + workspace: + clean: all + displayName: ${{ coalesce(project.desc, project.name) }} Material3 (API ${{ api }}) + pool: ${{ parameters.androidLinuxPool }} + variables: + REQUIRED_XCODE: $(DEVICETESTS_REQUIRED_XCODE) + APPIUM_HOME: $(System.DefaultWorkingDirectory)/.appium/ + steps: + - template: ui-tests-steps.yml + parameters: + platform: android + version: ${{ api }} + path: ${{ project.android }} + app: ${{ project.app }} + ${{ if eq(api, 27) }}: + device: android-emulator-32_${{ api }} + ${{ if not(eq(api, 27)) }}: + device: android-emulator-64_${{ api }} + deviceType: "pixel_3_xl" + provisionatorChannel: ${{ parameters.provisionatorChannel }} + useMaterial3: true + testFilter: "Material3" + skipProvisioning: ${{ parameters.skipProvisioning }} + + # Collect and publish Android Material3 snapshot diffs + - template: ui-tests-collect-snapshot-diffs.yml + parameters: + platform: 'Android Material3' + artifactName: 'uitest-snapshot-results-android-material3-$(System.StageName)-$(System.JobName)-$(System.JobAttempt)' + - stage: ios_ui_tests_mono displayName: iOS UITests Mono dependsOn: build_ui_tests diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3CheckBox_DefaultAppearance.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3CheckBox_DefaultAppearance.png new file mode 100644 index 000000000000..1244257ba261 Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android-notch-36/Material3CheckBox_DefaultAppearance.png differ diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Material3CheckBoxDefaultAppearance.xaml b/src/Controls/tests/TestCases.HostApp/Issues/Material3CheckBoxDefaultAppearance.xaml new file mode 100644 index 000000000000..a63f90fddda5 --- /dev/null +++ b/src/Controls/tests/TestCases.HostApp/Issues/Material3CheckBoxDefaultAppearance.xaml @@ -0,0 +1,21 @@ + + + + + +