diff --git a/azure-pipelines.yml b/azure-pipelines.yml
index d528d40c3f..8753cf18d6 100644
--- a/azure-pipelines.yml
+++ b/azure-pipelines.yml
@@ -8,6 +8,7 @@ variables:
NugetPackageVersionMaps: '$(CurrentSemanticVersion)'
TOOLKIT_NET_VERSION: '9.0.102'
LATEST_NET_VERSION: '9.0.x'
+ TFM_NET_VERSION: 'net9.0'
PathToLibrarySolution: 'src/CommunityToolkit.Maui.sln'
PathToSamplesSolution: 'samples/CommunityToolkit.Maui.Sample.sln'
PathToCommunityToolkitCsproj: 'src/CommunityToolkit.Maui/CommunityToolkit.Maui.csproj'
@@ -17,6 +18,8 @@ variables:
PathToCommunityToolkitMapsCsproj: 'src/CommunityToolkit.Maui.Maps/CommunityToolkit.Maui.Maps.csproj'
PathToCommunityToolkitSampleCsproj: 'samples/CommunityToolkit.Maui.Sample/CommunityToolkit.Maui.Sample.csproj'
PathToCommunityToolkitUnitTestCsproj: 'src/CommunityToolkit.Maui.UnitTests/CommunityToolkit.Maui.UnitTests.csproj'
+ PathToCommunityToolkitDeviceTestCsproj: 'src/CommunityToolkit.Maui.DeviceTests/CommunityToolkit.Maui.DeviceTests.csproj'
+ PathToCommunityToolkitDeviceTestBin: 'src/CommunityToolkit.Maui.DeviceTests/bin'
PathToCommunityToolkitAnalyzersCsproj: 'src/CommunityToolkit.Maui.Analyzers/CommunityToolkit.Maui.Analyzers.csproj'
PathToCommunityToolkitCameraAnalyzersCsproj: 'src/CommunityToolkit.Maui.Camera.Analyzers/CommunityToolkit.Maui.Camera.Analyzers.csproj'
PathToCommunityToolkitMediaElementAnalyzersCsproj: 'src/CommunityToolkit.Maui.MediaElement.Analyzers/CommunityToolkit.Maui.MediaElement.Analyzers.csproj'
@@ -228,6 +231,11 @@ jobs:
.\workload-install.ps1
displayName: Install Tizen Workload
+ - task: CmdLine@2
+ displayName: 'Install Microsoft.DotNet.XHarness.CLI'
+ inputs:
+ script: dotnet tool install Microsoft.DotNet.XHarness.CLI --global --version "*-*" --add-source https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-eng/nuget/v3/index.json
+
# Print Information on the .NET SDK Used By the CI Build Host
# These logs are useful information when debugging CI Builds
# Note: This step doesn't execute nor modify any code; it is strictly used for logging + debugging purposes
@@ -308,6 +316,13 @@ jobs:
inputs:
script: 'dotnet test $(PathToCommunityToolkitAnalyzersUnitTestCsproj) -c Release'
+ - task: CmdLine@2
+ displayName: 'Run CommunityToolkit.Maui.Analyzers.UnitTests'
+ env:
+ VSTEST_TESTHOST_SHUTDOWN_TIMEOUT: 1100 # Fixes "The active test run was aborted. Reason: Test host process crashed" https://dev.azure.com/dotnet/CommunityToolkit/_build/results?buildId=109660&view=logs&j=3f96dcf5-6e1e-5485-3200-c557d5216be3&t=12286b69-c788-55db-0a8c-ef899858fbe6&l=76 (source: https://github.com/microsoft/vstest/issues/2952#issuecomment-2234253765): $(sauceUsername)
+ inputs:
+ script: 'dotnet test $(PathToCommunityToolkitAnalyzersUnitTestCsproj) -c Release'
+
- task: CmdLine@2
displayName: 'Run CommunityToolkit.Maui.UnitTests'
env:
@@ -315,6 +330,41 @@ jobs:
inputs:
script: 'dotnet test $(PathToCommunityToolkitUnitTestCsproj) -c Release --settings ".runsettings" --collect "XPlat code coverage" --logger trx --results-directory $(Agent.TempDirectory)'
+ # Currently failing "Failed to find/create suitable simulator"
+ - task: CmdLine@2
+ condition: eq(variables['Agent.OS'], 'Darwin') # Only run this step on macOS
+ displayName: 'Run iOS UI Tests'
+ inputs:
+ script: |
+ dotnet build $(PathToCommunityToolkitDeviceTestCsproj) -r iossimulator-x64 -f $(TFM_NET_VERSION)-ios -c Release
+ xharness apple test --target ios-simulator-64 --app $(PathToCommunityToolkitDeviceTestBin)/Release/$(TFM_NET_VERSION)-ios/iossimulator-x64/CommunityToolkit.Maui.DeviceTests.app --output-directory $(Agent.TempDirectory)
+
+
+ - task: CmdLine@2
+ condition: eq(variables['Agent.OS'], 'Darwin') # Only run this step on macOS
+ displayName: 'Run MacCatalyst UI Tests'
+ inputs:
+ script: |
+ dotnet build $(PathToCommunityToolkitDeviceTestCsproj) -r maccatalyst-x64 -f $(TFM_NET_VERSION)-maccatalyst -c Release
+ xharness apple test --target maccatalyst --app $(PathToCommunityToolkitDeviceTestBin)/Release/$(TFM_NET_VERSION)-maccatalyst/maccatalyst-x64/CommunityToolkit.Maui.DeviceTests.app --output-directory $(Agent.TempDirectory)
+
+ # Currently failing: Failed to find compatible device: x86_64
+ - task: CmdLine@2
+ displayName: 'Run Android UI Tests'
+ inputs:
+ script: |
+ dotnet publish $(PathToCommunityToolkitDeviceTestCsproj) -r android-x64 -f $(TFM_NET_VERSION)-android -c Release
+
+ dotnet tool install --global AndroidSDK.Tool
+ android device list --format json
+ android sdk list
+ android sdk install --package emulator --package "system-images;android-33;google_apis;x86_64"
+ android avd create --name TestRunnerEmulator --sdk "system-images;android-33;google_apis;x86_64" --device pixel
+ android avd start --name TestRunnerEmulator --wait-boot
+
+ xharness android test --app $(PathToCommunityToolkitDeviceTestBin)/Release/$(TFM_NET_VERSION)-android/android-x64/publish/com.companyname.communitytoolkit.maui.devicetests-Signed.apk --package-name com.companyname.communitytoolkit.maui.devicetests --instrumentation devicerunners.xharness.maui.XHarnessInstrumentation --output-directory $(Agent.TempDirectory)'
+ xharness android adb -- emu kill
+
- task: PublishTestResults@2
condition: eq(variables['Agent.OS'], 'Windows_NT') # Only run this step on Windows
displayName: 'Publish Test Results'
diff --git a/samples/CommunityToolkit.Maui.Sample.sln b/samples/CommunityToolkit.Maui.Sample.sln
index 880a72a736..a7a7f743dc 100644
--- a/samples/CommunityToolkit.Maui.Sample.sln
+++ b/samples/CommunityToolkit.Maui.Sample.sln
@@ -53,6 +53,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Benchmarks", "Benchmarks",
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CommunityToolkit.Maui.Analyzers.Benchmarks", "..\src\CommunityToolkit.Maui.Analyzers.Benchmarks\CommunityToolkit.Maui.Analyzers.Benchmarks.csproj", "{B80F59B7-276C-4A55-A8DD-54587C8BC3D2}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CommunityToolkit.Maui.DeviceTests", "..\src\CommunityToolkit.Maui.DeviceTests\CommunityToolkit.Maui.DeviceTests.csproj", "{9CA70DAD-C6AE-4986-9F85-47FBE444071D}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -131,6 +133,10 @@ Global
{B80F59B7-276C-4A55-A8DD-54587C8BC3D2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B80F59B7-276C-4A55-A8DD-54587C8BC3D2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B80F59B7-276C-4A55-A8DD-54587C8BC3D2}.Release|Any CPU.Build.0 = Release|Any CPU
+ {9CA70DAD-C6AE-4986-9F85-47FBE444071D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {9CA70DAD-C6AE-4986-9F85-47FBE444071D}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {9CA70DAD-C6AE-4986-9F85-47FBE444071D}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {9CA70DAD-C6AE-4986-9F85-47FBE444071D}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -148,6 +154,7 @@ Global
{372D6A40-A4E0-434A-A463-C001441C68EB} = {9BFC4026-BC8F-43E2-BAA9-5BC2D764D37D}
{02C5B93A-B8D6-421D-B0EA-D0CC41A00F0B} = {9BFC4026-BC8F-43E2-BAA9-5BC2D764D37D}
{B80F59B7-276C-4A55-A8DD-54587C8BC3D2} = {ED5A9C0B-D270-442D-BABE-F4FF622926C8}
+ {9CA70DAD-C6AE-4986-9F85-47FBE444071D} = {9F7D54C0-EA17-409A-804F-B2E8D7F4A7F3}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {1E9E61C1-5CB7-4C8E-87BA-6C1D38238679}
diff --git a/src/CommunityToolkit.Maui.DeviceTests/App.xaml b/src/CommunityToolkit.Maui.DeviceTests/App.xaml
new file mode 100644
index 0000000000..02a216b6e3
--- /dev/null
+++ b/src/CommunityToolkit.Maui.DeviceTests/App.xaml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/CommunityToolkit.Maui.DeviceTests/App.xaml.cs b/src/CommunityToolkit.Maui.DeviceTests/App.xaml.cs
new file mode 100644
index 0000000000..fb84c57caa
--- /dev/null
+++ b/src/CommunityToolkit.Maui.DeviceTests/App.xaml.cs
@@ -0,0 +1,11 @@
+namespace CommunityToolkit.Maui.DeviceTests;
+
+public partial class App : Application
+{
+ public App()
+ {
+ InitializeComponent();
+
+ MainPage = new AppShell();
+ }
+}
diff --git a/src/CommunityToolkit.Maui.DeviceTests/AppShell.xaml b/src/CommunityToolkit.Maui.DeviceTests/AppShell.xaml
new file mode 100644
index 0000000000..1319bbae8e
--- /dev/null
+++ b/src/CommunityToolkit.Maui.DeviceTests/AppShell.xaml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
diff --git a/src/CommunityToolkit.Maui.DeviceTests/AppShell.xaml.cs b/src/CommunityToolkit.Maui.DeviceTests/AppShell.xaml.cs
new file mode 100644
index 0000000000..001a7a1c5d
--- /dev/null
+++ b/src/CommunityToolkit.Maui.DeviceTests/AppShell.xaml.cs
@@ -0,0 +1,9 @@
+namespace CommunityToolkit.Maui.DeviceTests;
+
+public partial class AppShell : Shell
+{
+ public AppShell()
+ {
+ InitializeComponent();
+ }
+}
diff --git a/src/CommunityToolkit.Maui.DeviceTests/BaseUITest.cs b/src/CommunityToolkit.Maui.DeviceTests/BaseUITest.cs
new file mode 100644
index 0000000000..73451eff3e
--- /dev/null
+++ b/src/CommunityToolkit.Maui.DeviceTests/BaseUITest.cs
@@ -0,0 +1,49 @@
+using Xunit;
+
+namespace CommunityToolkit.Maui.DeviceTests;
+[Collection("UITests")]
+public abstract class UITests : IAsyncLifetime
+ where T : Page
+{
+ protected T CurrentPage { get; private set; } = default!;
+
+ protected IMauiContext MauiContext { get; private set; } = default!;
+
+ public async Task InitializeAsync()
+ {
+ Routing.RegisterRoute("uitests", typeof(T));
+
+ await Shell.Current.GoToAsync("uitests");
+
+ CurrentPage = (T)Shell.Current.CurrentPage;
+ MauiContext = CurrentPage.Handler!.MauiContext!;
+ if (CurrentPage.IsLoaded)
+ {
+ return;
+ }
+
+ var tcs = new TaskCompletionSource();
+ CurrentPage.Loaded += OnLoaded;
+
+ await Task.WhenAny(tcs.Task, Task.Delay(1000));
+
+ CurrentPage.Loaded -= OnLoaded;
+
+ Assert.True(CurrentPage.IsLoaded);
+
+ void OnLoaded(object? sender, EventArgs e)
+ {
+ CurrentPage.Loaded -= OnLoaded;
+ tcs.SetResult();
+ }
+ }
+
+ public async Task DisposeAsync()
+ {
+ CurrentPage = null!;
+
+ await Shell.Current.GoToAsync("..");
+
+ Routing.UnRegisterRoute("uitests");
+ }
+}
diff --git a/src/CommunityToolkit.Maui.DeviceTests/CommunityToolkit.Maui.DeviceTests.csproj b/src/CommunityToolkit.Maui.DeviceTests/CommunityToolkit.Maui.DeviceTests.csproj
new file mode 100644
index 0000000000..a74ad576d4
--- /dev/null
+++ b/src/CommunityToolkit.Maui.DeviceTests/CommunityToolkit.Maui.DeviceTests.csproj
@@ -0,0 +1,70 @@
+
+
+
+ $(NetVersion)-ios;$(NetVersion)-android;$(NetVersion)-maccatalyst
+ $(TargetFrameworks);$(NetVersion)-windows10.0.19041.0
+
+
+ Exe
+ CommunityToolkit.Maui.DeviceTests
+ true
+ true
+
+
+ CommunityToolkit.Maui.DeviceTests
+
+
+ com.microsoft.CommunityToolkit.Maui.DeviceTests
+ b9372eea-e8b8-45fb-b785-6ffb8eb6c099
+
+
+ 1.0
+ 1
+
+ 11.0
+ 13.1
+ 21.0
+ 10.0.17763.0
+ 10.0.17763.0
+ 6.5
+ $(NoWarn);NU1605
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/CommunityToolkit.Maui.DeviceTests/MainPage.xaml b/src/CommunityToolkit.Maui.DeviceTests/MainPage.xaml
new file mode 100644
index 0000000000..443fda4102
--- /dev/null
+++ b/src/CommunityToolkit.Maui.DeviceTests/MainPage.xaml
@@ -0,0 +1,41 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/CommunityToolkit.Maui.DeviceTests/MainPage.xaml.cs b/src/CommunityToolkit.Maui.DeviceTests/MainPage.xaml.cs
new file mode 100644
index 0000000000..83aaf58c78
--- /dev/null
+++ b/src/CommunityToolkit.Maui.DeviceTests/MainPage.xaml.cs
@@ -0,0 +1,28 @@
+namespace CommunityToolkit.Maui.DeviceTests;
+
+public partial class MainPage : ContentPage
+{
+ int count = 0;
+
+ public MainPage()
+ {
+ InitializeComponent();
+ }
+
+ private void OnCounterClicked(object sender, EventArgs e)
+ {
+ count++;
+
+ if (count == 1)
+ {
+ CounterBtn.Text = $"Clicked {count} time";
+ }
+ else
+ {
+ CounterBtn.Text = $"Clicked {count} times";
+ }
+
+ SemanticScreenReader.Announce(CounterBtn.Text);
+ }
+}
+
diff --git a/src/CommunityToolkit.Maui.DeviceTests/MauiProgram.cs b/src/CommunityToolkit.Maui.DeviceTests/MauiProgram.cs
new file mode 100644
index 0000000000..3edf55ffa9
--- /dev/null
+++ b/src/CommunityToolkit.Maui.DeviceTests/MauiProgram.cs
@@ -0,0 +1,34 @@
+using CommunityToolkit.Maui.DeviceTests.Tests;
+using DeviceRunners.UITesting;
+using DeviceRunners.VisualRunners;
+using DeviceRunners.XHarness;
+using Microsoft.Extensions.Logging;
+
+namespace CommunityToolkit.Maui.DeviceTests;
+public static class MauiProgram
+{
+ public static MauiApp CreateMauiApp()
+ {
+ var builder = MauiApp.CreateBuilder();
+ builder
+ .ConfigureUITesting()
+ .UseXHarnessTestRunner(conf => conf
+ .AddTestAssembly(typeof(MauiProgram).Assembly)
+ .AddXunit())
+ .UseVisualTestRunner(conf => conf
+ .AddTestAssembly(typeof(MauiProgram).Assembly)
+ .AddXunit())
+ .UseMauiApp()
+ .ConfigureFonts(fonts =>
+ {
+ fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
+ fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
+ });
+
+#if DEBUG
+ builder.Logging.AddDebug();
+#endif
+
+ return builder.Build();
+ }
+}
diff --git a/src/CommunityToolkit.Maui.DeviceTests/NuGet.config b/src/CommunityToolkit.Maui.DeviceTests/NuGet.config
new file mode 100644
index 0000000000..6d52e76445
--- /dev/null
+++ b/src/CommunityToolkit.Maui.DeviceTests/NuGet.config
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/CommunityToolkit.Maui.DeviceTests/Platforms/Android/AndroidManifest.xml b/src/CommunityToolkit.Maui.DeviceTests/Platforms/Android/AndroidManifest.xml
new file mode 100644
index 0000000000..e9937ad77d
--- /dev/null
+++ b/src/CommunityToolkit.Maui.DeviceTests/Platforms/Android/AndroidManifest.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/CommunityToolkit.Maui.DeviceTests/Platforms/Android/MainActivity.cs b/src/CommunityToolkit.Maui.DeviceTests/Platforms/Android/MainActivity.cs
new file mode 100644
index 0000000000..0130681008
--- /dev/null
+++ b/src/CommunityToolkit.Maui.DeviceTests/Platforms/Android/MainActivity.cs
@@ -0,0 +1,9 @@
+using Android.App;
+using Android.Content.PM;
+using Android.OS;
+
+namespace CommunityToolkit.Maui.DeviceTests;
+[Activity(Theme = "@style/Maui.SplashTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation | ConfigChanges.UiMode | ConfigChanges.ScreenLayout | ConfigChanges.SmallestScreenSize | ConfigChanges.Density)]
+public class MainActivity : MauiAppCompatActivity
+{
+}
diff --git a/src/CommunityToolkit.Maui.DeviceTests/Platforms/Android/MainApplication.cs b/src/CommunityToolkit.Maui.DeviceTests/Platforms/Android/MainApplication.cs
new file mode 100644
index 0000000000..76c4f69975
--- /dev/null
+++ b/src/CommunityToolkit.Maui.DeviceTests/Platforms/Android/MainApplication.cs
@@ -0,0 +1,14 @@
+using Android.App;
+using Android.Runtime;
+
+namespace CommunityToolkit.Maui.DeviceTests;
+[Application]
+public class MainApplication : MauiApplication
+{
+ public MainApplication(IntPtr handle, JniHandleOwnership ownership)
+ : base(handle, ownership)
+ {
+ }
+
+ protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
+}
diff --git a/src/CommunityToolkit.Maui.DeviceTests/Platforms/Android/Resources/values/colors.xml b/src/CommunityToolkit.Maui.DeviceTests/Platforms/Android/Resources/values/colors.xml
new file mode 100644
index 0000000000..c04d7492ab
--- /dev/null
+++ b/src/CommunityToolkit.Maui.DeviceTests/Platforms/Android/Resources/values/colors.xml
@@ -0,0 +1,6 @@
+
+
+ #512BD4
+ #2B0B98
+ #2B0B98
+
\ No newline at end of file
diff --git a/src/CommunityToolkit.Maui.DeviceTests/Platforms/MacCatalyst/AppDelegate.cs b/src/CommunityToolkit.Maui.DeviceTests/Platforms/MacCatalyst/AppDelegate.cs
new file mode 100644
index 0000000000..f191e1acba
--- /dev/null
+++ b/src/CommunityToolkit.Maui.DeviceTests/Platforms/MacCatalyst/AppDelegate.cs
@@ -0,0 +1,8 @@
+using Foundation;
+
+namespace CommunityToolkit.Maui.DeviceTests;
+[Register("AppDelegate")]
+public partial class AppDelegate : MauiUIApplicationDelegate
+{
+ protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
+}
diff --git a/src/CommunityToolkit.Maui.DeviceTests/Platforms/MacCatalyst/Info.plist b/src/CommunityToolkit.Maui.DeviceTests/Platforms/MacCatalyst/Info.plist
new file mode 100644
index 0000000000..c96dd0a225
--- /dev/null
+++ b/src/CommunityToolkit.Maui.DeviceTests/Platforms/MacCatalyst/Info.plist
@@ -0,0 +1,30 @@
+
+
+
+
+ UIDeviceFamily
+
+ 1
+ 2
+
+ UIRequiredDeviceCapabilities
+
+ arm64
+
+ UISupportedInterfaceOrientations
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+ UISupportedInterfaceOrientations~ipad
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationPortraitUpsideDown
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+ XSAppIconAssets
+ Assets.xcassets/appicon.appiconset
+
+
diff --git a/src/CommunityToolkit.Maui.DeviceTests/Platforms/MacCatalyst/Program.cs b/src/CommunityToolkit.Maui.DeviceTests/Platforms/MacCatalyst/Program.cs
new file mode 100644
index 0000000000..6a69f4adae
--- /dev/null
+++ b/src/CommunityToolkit.Maui.DeviceTests/Platforms/MacCatalyst/Program.cs
@@ -0,0 +1,14 @@
+using ObjCRuntime;
+using UIKit;
+
+namespace CommunityToolkit.Maui.DeviceTests;
+public class Program
+{
+ // This is the main entry point of the application.
+ static void Main(string[] args)
+ {
+ // if you want to use a different Application Delegate class from "AppDelegate"
+ // you can specify it here.
+ UIApplication.Main(args, null, typeof(AppDelegate));
+ }
+}
diff --git a/src/CommunityToolkit.Maui.DeviceTests/Platforms/Tizen/Main.cs b/src/CommunityToolkit.Maui.DeviceTests/Platforms/Tizen/Main.cs
new file mode 100644
index 0000000000..56a4a7a816
--- /dev/null
+++ b/src/CommunityToolkit.Maui.DeviceTests/Platforms/Tizen/Main.cs
@@ -0,0 +1,15 @@
+using System;
+using Microsoft.Maui;
+using Microsoft.Maui.Hosting;
+
+namespace CommunityToolkit.Maui.DeviceTests;
+class Program : MauiApplication
+{
+ protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
+
+ static void Main(string[] args)
+ {
+ var app = new Program();
+ app.Run(args);
+ }
+}
diff --git a/src/CommunityToolkit.Maui.DeviceTests/Platforms/Tizen/tizen-manifest.xml b/src/CommunityToolkit.Maui.DeviceTests/Platforms/Tizen/tizen-manifest.xml
new file mode 100644
index 0000000000..ec0ab7bfb0
--- /dev/null
+++ b/src/CommunityToolkit.Maui.DeviceTests/Platforms/Tizen/tizen-manifest.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+ maui-appicon-placeholder
+
+
+
+
+ http://tizen.org/privilege/internet
+
+
+
+
\ No newline at end of file
diff --git a/src/CommunityToolkit.Maui.DeviceTests/Platforms/Windows/App.xaml b/src/CommunityToolkit.Maui.DeviceTests/Platforms/Windows/App.xaml
new file mode 100644
index 0000000000..06445a920e
--- /dev/null
+++ b/src/CommunityToolkit.Maui.DeviceTests/Platforms/Windows/App.xaml
@@ -0,0 +1,8 @@
+
+
+
diff --git a/src/CommunityToolkit.Maui.DeviceTests/Platforms/Windows/App.xaml.cs b/src/CommunityToolkit.Maui.DeviceTests/Platforms/Windows/App.xaml.cs
new file mode 100644
index 0000000000..79b9c2d781
--- /dev/null
+++ b/src/CommunityToolkit.Maui.DeviceTests/Platforms/Windows/App.xaml.cs
@@ -0,0 +1,23 @@
+using Microsoft.UI.Xaml;
+
+// To learn more about WinUI, the WinUI project structure,
+// and more about our project templates, see: http://aka.ms/winui-project-info.
+
+namespace CommunityToolkit.Maui.DeviceTests.WinUI;
+///
+/// Provides application-specific behavior to supplement the default Application class.
+///
+public partial class App : MauiWinUIApplication
+{
+ ///
+ /// Initializes the singleton application object. This is the first line of authored code
+ /// executed, and as such is the logical equivalent of main() or WinMain().
+ ///
+ public App()
+ {
+ this.InitializeComponent();
+ }
+
+ protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
+}
+
diff --git a/src/CommunityToolkit.Maui.DeviceTests/Platforms/Windows/Package.appxmanifest b/src/CommunityToolkit.Maui.DeviceTests/Platforms/Windows/Package.appxmanifest
new file mode 100644
index 0000000000..890a69c639
--- /dev/null
+++ b/src/CommunityToolkit.Maui.DeviceTests/Platforms/Windows/Package.appxmanifest
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+
+
+ $placeholder$
+ User Name
+ $placeholder$.png
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/CommunityToolkit.Maui.DeviceTests/Platforms/Windows/app.manifest b/src/CommunityToolkit.Maui.DeviceTests/Platforms/Windows/app.manifest
new file mode 100644
index 0000000000..feb1ca73ab
--- /dev/null
+++ b/src/CommunityToolkit.Maui.DeviceTests/Platforms/Windows/app.manifest
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+ true/PM
+ PerMonitorV2, PerMonitor
+
+
+
diff --git a/src/CommunityToolkit.Maui.DeviceTests/Platforms/iOS/AppDelegate.cs b/src/CommunityToolkit.Maui.DeviceTests/Platforms/iOS/AppDelegate.cs
new file mode 100644
index 0000000000..89fdb862bc
--- /dev/null
+++ b/src/CommunityToolkit.Maui.DeviceTests/Platforms/iOS/AppDelegate.cs
@@ -0,0 +1,8 @@
+using Foundation;
+
+namespace CommunityToolkit.Maui.DeviceTests;
+[Register("AppDelegate")]
+public class AppDelegate : MauiUIApplicationDelegate
+{
+ protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
+}
diff --git a/src/CommunityToolkit.Maui.DeviceTests/Platforms/iOS/Info.plist b/src/CommunityToolkit.Maui.DeviceTests/Platforms/iOS/Info.plist
new file mode 100644
index 0000000000..0004a4fdee
--- /dev/null
+++ b/src/CommunityToolkit.Maui.DeviceTests/Platforms/iOS/Info.plist
@@ -0,0 +1,32 @@
+
+
+
+
+ LSRequiresIPhoneOS
+
+ UIDeviceFamily
+
+ 1
+ 2
+
+ UIRequiredDeviceCapabilities
+
+ arm64
+
+ UISupportedInterfaceOrientations
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+ UISupportedInterfaceOrientations~ipad
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationPortraitUpsideDown
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+ XSAppIconAssets
+ Assets.xcassets/appicon.appiconset
+
+
diff --git a/src/CommunityToolkit.Maui.DeviceTests/Platforms/iOS/Program.cs b/src/CommunityToolkit.Maui.DeviceTests/Platforms/iOS/Program.cs
new file mode 100644
index 0000000000..6a69f4adae
--- /dev/null
+++ b/src/CommunityToolkit.Maui.DeviceTests/Platforms/iOS/Program.cs
@@ -0,0 +1,14 @@
+using ObjCRuntime;
+using UIKit;
+
+namespace CommunityToolkit.Maui.DeviceTests;
+public class Program
+{
+ // This is the main entry point of the application.
+ static void Main(string[] args)
+ {
+ // if you want to use a different Application Delegate class from "AppDelegate"
+ // you can specify it here.
+ UIApplication.Main(args, null, typeof(AppDelegate));
+ }
+}
diff --git a/src/CommunityToolkit.Maui.DeviceTests/Properties/launchSettings.json b/src/CommunityToolkit.Maui.DeviceTests/Properties/launchSettings.json
new file mode 100644
index 0000000000..edf8aadcc8
--- /dev/null
+++ b/src/CommunityToolkit.Maui.DeviceTests/Properties/launchSettings.json
@@ -0,0 +1,8 @@
+{
+ "profiles": {
+ "Windows Machine": {
+ "commandName": "MsixPackage",
+ "nativeDebugging": false
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/CommunityToolkit.Maui.DeviceTests/Resources/AppIcon/appicon.svg b/src/CommunityToolkit.Maui.DeviceTests/Resources/AppIcon/appicon.svg
new file mode 100644
index 0000000000..9d63b6513a
--- /dev/null
+++ b/src/CommunityToolkit.Maui.DeviceTests/Resources/AppIcon/appicon.svg
@@ -0,0 +1,4 @@
+
+
\ No newline at end of file
diff --git a/src/CommunityToolkit.Maui.DeviceTests/Resources/AppIcon/appiconfg.svg b/src/CommunityToolkit.Maui.DeviceTests/Resources/AppIcon/appiconfg.svg
new file mode 100644
index 0000000000..21dfb25f18
--- /dev/null
+++ b/src/CommunityToolkit.Maui.DeviceTests/Resources/AppIcon/appiconfg.svg
@@ -0,0 +1,8 @@
+
+
+
\ No newline at end of file
diff --git a/src/CommunityToolkit.Maui.DeviceTests/Resources/Fonts/OpenSans-Regular.ttf b/src/CommunityToolkit.Maui.DeviceTests/Resources/Fonts/OpenSans-Regular.ttf
new file mode 100644
index 0000000000..1e57f677e6
Binary files /dev/null and b/src/CommunityToolkit.Maui.DeviceTests/Resources/Fonts/OpenSans-Regular.ttf differ
diff --git a/src/CommunityToolkit.Maui.DeviceTests/Resources/Fonts/OpenSans-Semibold.ttf b/src/CommunityToolkit.Maui.DeviceTests/Resources/Fonts/OpenSans-Semibold.ttf
new file mode 100644
index 0000000000..7689b81390
Binary files /dev/null and b/src/CommunityToolkit.Maui.DeviceTests/Resources/Fonts/OpenSans-Semibold.ttf differ
diff --git a/src/CommunityToolkit.Maui.DeviceTests/Resources/Images/dotnet_bot.svg b/src/CommunityToolkit.Maui.DeviceTests/Resources/Images/dotnet_bot.svg
new file mode 100644
index 0000000000..abfaff26a8
--- /dev/null
+++ b/src/CommunityToolkit.Maui.DeviceTests/Resources/Images/dotnet_bot.svg
@@ -0,0 +1,93 @@
+
diff --git a/src/CommunityToolkit.Maui.DeviceTests/Resources/Raw/AboutAssets.txt b/src/CommunityToolkit.Maui.DeviceTests/Resources/Raw/AboutAssets.txt
new file mode 100644
index 0000000000..15d6244845
--- /dev/null
+++ b/src/CommunityToolkit.Maui.DeviceTests/Resources/Raw/AboutAssets.txt
@@ -0,0 +1,15 @@
+Any raw assets you want to be deployed with your application can be placed in
+this directory (and child directories). Deployment of the asset to your application
+is automatically handled by the following `MauiAsset` Build Action within your `.csproj`.
+
+
+
+These files will be deployed with you package and will be accessible using Essentials:
+
+ async Task LoadMauiAsset()
+ {
+ using var stream = await FileSystem.OpenAppPackageFileAsync("AboutAssets.txt");
+ using var reader = new StreamReader(stream);
+
+ var contents = reader.ReadToEnd();
+ }
diff --git a/src/CommunityToolkit.Maui.DeviceTests/Resources/Splash/splash.svg b/src/CommunityToolkit.Maui.DeviceTests/Resources/Splash/splash.svg
new file mode 100644
index 0000000000..21dfb25f18
--- /dev/null
+++ b/src/CommunityToolkit.Maui.DeviceTests/Resources/Splash/splash.svg
@@ -0,0 +1,8 @@
+
+
+
\ No newline at end of file
diff --git a/src/CommunityToolkit.Maui.DeviceTests/Resources/Styles/Colors.xaml b/src/CommunityToolkit.Maui.DeviceTests/Resources/Styles/Colors.xaml
new file mode 100644
index 0000000000..245758ba17
--- /dev/null
+++ b/src/CommunityToolkit.Maui.DeviceTests/Resources/Styles/Colors.xaml
@@ -0,0 +1,44 @@
+
+
+
+
+ #512BD4
+ #DFD8F7
+ #2B0B98
+ White
+ Black
+ #E1E1E1
+ #C8C8C8
+ #ACACAC
+ #919191
+ #6E6E6E
+ #404040
+ #212121
+ #141414
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ #F7B548
+ #FFD590
+ #FFE5B9
+ #28C2D1
+ #7BDDEF
+ #C3F2F4
+ #3E8EED
+ #72ACF1
+ #A7CBF6
+
+
\ No newline at end of file
diff --git a/src/CommunityToolkit.Maui.DeviceTests/Resources/Styles/Styles.xaml b/src/CommunityToolkit.Maui.DeviceTests/Resources/Styles/Styles.xaml
new file mode 100644
index 0000000000..dc4a03475b
--- /dev/null
+++ b/src/CommunityToolkit.Maui.DeviceTests/Resources/Styles/Styles.xaml
@@ -0,0 +1,405 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/CommunityToolkit.Maui.DeviceTests/Tests/BehaviorTest.cs b/src/CommunityToolkit.Maui.DeviceTests/Tests/BehaviorTest.cs
new file mode 100644
index 0000000000..b9c687c5af
--- /dev/null
+++ b/src/CommunityToolkit.Maui.DeviceTests/Tests/BehaviorTest.cs
@@ -0,0 +1,29 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using CommunityToolkit.Maui.Behaviors;
+using Xunit;
+
+namespace CommunityToolkit.Maui.DeviceTests.Tests;
+public class BehaviorTest
+{
+ [Fact]
+ public void SuccessfulTest()
+ {
+ Assert.True(true);
+ }
+
+ [Fact]
+ public void SomeBehavior()
+ {
+ var behavior = new IconTintColorBehavior();
+ var expectedColor = Colors.Fuchsia;
+ behavior.TintColor = expectedColor;
+
+ var appliedColor = behavior.GetValue(IconTintColorBehavior.TintColorProperty) as Color;
+
+ Assert.Equal(expectedColor, appliedColor);
+ }
+}
diff --git a/src/CommunityToolkit.Maui.DeviceTests/Tests/UITests/StatusBarTestPage.xaml b/src/CommunityToolkit.Maui.DeviceTests/Tests/UITests/StatusBarTestPage.xaml
new file mode 100644
index 0000000000..ba76a332bc
--- /dev/null
+++ b/src/CommunityToolkit.Maui.DeviceTests/Tests/UITests/StatusBarTestPage.xaml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/CommunityToolkit.Maui.DeviceTests/Tests/UITests/StatusBarTestPage.xaml.cs b/src/CommunityToolkit.Maui.DeviceTests/Tests/UITests/StatusBarTestPage.xaml.cs
new file mode 100644
index 0000000000..5043734c95
--- /dev/null
+++ b/src/CommunityToolkit.Maui.DeviceTests/Tests/UITests/StatusBarTestPage.xaml.cs
@@ -0,0 +1,9 @@
+namespace CommunityToolkit.Maui.DeviceTests.Tests.UITests;
+
+public partial class StatusBarTestPage : ContentPage
+{
+ public StatusBarTestPage()
+ {
+ InitializeComponent();
+ }
+}
\ No newline at end of file
diff --git a/src/CommunityToolkit.Maui.DeviceTests/Tests/UITests/StatusBarUITest.cs b/src/CommunityToolkit.Maui.DeviceTests/Tests/UITests/StatusBarUITest.cs
new file mode 100644
index 0000000000..43ae831970
--- /dev/null
+++ b/src/CommunityToolkit.Maui.DeviceTests/Tests/UITests/StatusBarUITest.cs
@@ -0,0 +1,26 @@
+using CommunityToolkit.Maui.Behaviors;
+using Xunit;
+
+namespace CommunityToolkit.Maui.DeviceTests.Tests.UITests;
+public sealed class StatusBarUITest : UITests
+{
+ [UIFact]
+ public void IsBehaviorAttached()
+ {
+ var behavior = CurrentPage.Behaviors.FirstOrDefault( x => x is StatusBarBehavior);
+
+ Assert.NotNull(behavior);
+ }
+
+ [UIFact]
+ public void GetTheColor()
+ {
+ var behavior = CurrentPage.Behaviors.FirstOrDefault(x => x is StatusBarBehavior) as StatusBarBehavior;
+
+ Assert.NotNull(behavior);
+
+ var color = behavior.StatusBarColor;
+
+ Assert.Equal(Colors.Fuchsia, color);
+ }
+}