Skip to content

Commit

Permalink
chore: Windows DeviceTests
Browse files Browse the repository at this point in the history
  • Loading branch information
Pana committed Apr 16, 2024
1 parent 0a53c06 commit a637884
Show file tree
Hide file tree
Showing 10 changed files with 170 additions and 133 deletions.
240 changes: 120 additions & 120 deletions src/Controls/tests/DeviceTests/ControlsHandlerTestBase.Windows.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ Task SetupWindowForTests<THandler>(IWindow window, Func<Task> runTests, IMauiCon
var applicationContext = mauiContext.MakeApplicationScope(UI.Xaml.Application.Current);
var appStub = new MauiAppNewWindowStub(window);
UI.Xaml.Application.Current.SetApplicationHandler(appStub, applicationContext);
UI.Xaml.Application.Current.SetApplicationHandler2(appStub, applicationContext);
WWindow newWindow = null;
try
{
Expand All @@ -51,130 +51,130 @@ Task SetupWindowForTests<THandler>(IWindow window, Func<Task> runTests, IMauiCon
});
}

protected IEnumerable<WNavigationViewItem> GetNavigationViewItems(MauiNavigationView navigationView)
{
if (navigationView.MenuItems?.Count > 0)
{
foreach (var menuItem in navigationView.MenuItems)
{
if (menuItem is WNavigationViewItem item)
yield return item;
}
}
else if (navigationView.MenuItemsSource != null && navigationView.TopNavMenuItemsHost != null)
{
var itemCount = navigationView.TopNavMenuItemsHost.ItemsSourceView.Count;
for (int i = 0; i < itemCount; i++)
{
UI.Xaml.UIElement uIElement = navigationView.TopNavMenuItemsHost.TryGetElement(i);

if (uIElement is WNavigationViewItem item)
yield return item;
}
}
}

protected double DistanceYFromTheBottomOfTheAppTitleBar(IElement element)
{
var handler = element.Handler;
var rootManager = handler.MauiContext.GetNavigationRootManager();
var position = element.GetLocationRelativeTo(rootManager.AppTitleBar);
var distance = rootManager.AppTitleBar.ActualHeight - position.Value.Y;
return distance;
}
//protected IEnumerable<WNavigationViewItem> GetNavigationViewItems(MauiNavigationView navigationView)
//{
// if (navigationView.MenuItems?.Count > 0)
// {
// foreach (var menuItem in navigationView.MenuItems)
// {
// if (menuItem is WNavigationViewItem item)
// yield return item;
// }
// }
// else if (navigationView.MenuItemsSource != null && navigationView.TopNavMenuItemsHost != null)
// {
// var itemCount = navigationView.TopNavMenuItemsHost.ItemsSourceView.Count;
// for (int i = 0; i < itemCount; i++)
// {
// UI.Xaml.UIElement uIElement = navigationView.TopNavMenuItemsHost.TryGetElement(i);

// if (uIElement is WNavigationViewItem item)
// yield return item;
// }
// }
//}

//protected double DistanceYFromTheBottomOfTheAppTitleBar(IElement element)
//{
// var handler = element.Handler;
// var rootManager = handler.MauiContext.GetNavigationRootManager();
// var position = element.GetLocationRelativeTo(rootManager.AppTitleBar);
// var distance = rootManager.AppTitleBar.ActualHeight - position.Value.Y;
// return distance;
//}

MauiNavigationView GetMauiNavigationView(NavigationRootManager navigationRootManager)
{
return (navigationRootManager.RootView as WindowRootView).NavigationViewControl;
}

protected WindowRootView GetWindowRootView(IElementHandler handler)
{
return handler.MauiContext.GetNavigationRootManager().RootView as WindowRootView;
}

protected MauiNavigationView GetMauiNavigationView(IMauiContext mauiContext)
{
return GetMauiNavigationView(mauiContext.GetNavigationRootManager());
}

protected bool IsBackButtonVisible(IElementHandler handler) =>
IsBackButtonVisible(handler.MauiContext);

bool IsBackButtonVisible(IMauiContext mauiContext)
{
var navView = GetMauiNavigationView(mauiContext);
return navView.IsBackButtonVisible == UI.Xaml.Controls.NavigationViewBackButtonVisible.Visible;
}

public bool IsNavigationBarVisible(IElementHandler handler) =>
IsNavigationBarVisible(handler.MauiContext);

public bool IsNavigationBarVisible(IMauiContext mauiContext)
{
var header = GetPlatformToolbar(mauiContext);
return header?.Visibility == UI.Xaml.Visibility.Visible;
}

protected MauiToolbar GetPlatformToolbar(IMauiContext mauiContext)
{
var navView = (RootNavigationView)GetMauiNavigationView(mauiContext);
if (navView.PaneDisplayMode == NavigationViewPaneDisplayMode.Top)
return (MauiToolbar)navView.PaneFooter;

return (MauiToolbar)navView.Header;
}

protected MauiToolbar GetPlatformToolbar(IElementHandler handler) =>
GetPlatformToolbar(handler.MauiContext);

protected Size GetTitleViewExpectedSize(IElementHandler handler)
{
var headerView = GetPlatformToolbar(handler.MauiContext);
return new Size(headerView.ActualWidth, headerView.ActualHeight);
}

public bool ToolbarItemsMatch(
IElementHandler handler,
params ToolbarItem[] toolbarItems)
{
var primaryToolbarItems = toolbarItems.Where(x => x.Order != ToolbarItemOrder.Secondary).ToArray();
var secondaryToolbarItems = toolbarItems.Where(x => x.Order == ToolbarItemOrder.Secondary).ToArray();

var navView = (RootNavigationView)GetMauiNavigationView(handler.MauiContext);
MauiToolbar windowHeader = (MauiToolbar)navView.Header;

ValidateCommandBarCommands(windowHeader?.CommandBar?.PrimaryCommands, primaryToolbarItems);
ValidateCommandBarCommands(windowHeader?.CommandBar?.SecondaryCommands, secondaryToolbarItems);

void ValidateCommandBarCommands(IObservableVector<ICommandBarElement> commands, ToolbarItem[] orderToolbarItems)
{
if (orderToolbarItems.Length == 0)
{
Assert.True(commands is null || commands.Count == 0);
return;
}

Assert.NotNull(commands);
Assert.Equal(orderToolbarItems.Length, commands.Count);
for (var i = 0; i < toolbarItems.Length; i++)
{
ToolbarItem toolbarItem = orderToolbarItems[i];
var command = ((WAppBarButton)commands[i]);
Assert.Equal(toolbarItem, command.DataContext);
}
}

return true;
}

protected FrameworkElement GetTitleView(IElementHandler handler)
{
var toolbar = GetPlatformToolbar(handler);
return (FrameworkElement)toolbar.TitleView;
}

protected string GetToolbarTitle(IElementHandler handler) =>
GetPlatformToolbar(handler).Title;
//protected WindowRootView GetWindowRootView(IElementHandler handler)
//{
// return handler.MauiContext.GetNavigationRootManager().RootView as WindowRootView;
//}

//protected MauiNavigationView GetMauiNavigationView(IMauiContext mauiContext)
//{
// return GetMauiNavigationView(mauiContext.GetNavigationRootManager());
//}

//protected bool IsBackButtonVisible(IElementHandler handler) =>
// IsBackButtonVisible(handler.MauiContext);

//bool IsBackButtonVisible(IMauiContext mauiContext)
//{
// var navView = GetMauiNavigationView(mauiContext);
// return navView.IsBackButtonVisible == UI.Xaml.Controls.NavigationViewBackButtonVisible.Visible;
//}

//public bool IsNavigationBarVisible(IElementHandler handler) =>
// IsNavigationBarVisible(handler.MauiContext);

//public bool IsNavigationBarVisible(IMauiContext mauiContext)
//{
// var header = GetPlatformToolbar(mauiContext);
// return header?.Visibility == UI.Xaml.Visibility.Visible;
//}

//protected MauiToolbar GetPlatformToolbar(IMauiContext mauiContext)
//{
// var navView = (RootNavigationView)GetMauiNavigationView(mauiContext);
// if (navView.PaneDisplayMode == NavigationViewPaneDisplayMode.Top)
// return (MauiToolbar)navView.PaneFooter;

// return (MauiToolbar)navView.Header;
//}

//protected MauiToolbar GetPlatformToolbar(IElementHandler handler) =>
// GetPlatformToolbar(handler.MauiContext);

//protected Size GetTitleViewExpectedSize(IElementHandler handler)
//{
// var headerView = GetPlatformToolbar(handler.MauiContext);
// return new Size(headerView.ActualWidth, headerView.ActualHeight);
//}

//public bool ToolbarItemsMatch(
// IElementHandler handler,
// params ToolbarItem[] toolbarItems)
//{
// var primaryToolbarItems = toolbarItems.Where(x => x.Order != ToolbarItemOrder.Secondary).ToArray();
// var secondaryToolbarItems = toolbarItems.Where(x => x.Order == ToolbarItemOrder.Secondary).ToArray();

// var navView = (RootNavigationView)GetMauiNavigationView(handler.MauiContext);
// MauiToolbar windowHeader = (MauiToolbar)navView.Header;

// ValidateCommandBarCommands(windowHeader?.CommandBar?.PrimaryCommands, primaryToolbarItems);
// ValidateCommandBarCommands(windowHeader?.CommandBar?.SecondaryCommands, secondaryToolbarItems);

// void ValidateCommandBarCommands(IObservableVector<ICommandBarElement> commands, ToolbarItem[] orderToolbarItems)
// {
// if (orderToolbarItems.Length == 0)
// {
// Assert.True(commands is null || commands.Count == 0);
// return;
// }

// Assert.NotNull(commands);
// Assert.Equal(orderToolbarItems.Length, commands.Count);
// for (var i = 0; i < toolbarItems.Length; i++)
// {
// ToolbarItem toolbarItem = orderToolbarItems[i];
// var command = ((WAppBarButton)commands[i]);
// Assert.Equal(toolbarItem, command.DataContext);
// }
// }

// return true;
//}

//protected FrameworkElement GetTitleView(IElementHandler handler)
//{
// var toolbar = GetPlatformToolbar(handler);
// return (FrameworkElement)toolbar.TitleView;
//}

//protected string GetToolbarTitle(IElementHandler handler) =>
// GetPlatformToolbar(handler).Title;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,8 @@ void InvokeWindowDestroying()
{
if (Window is not null)
{
if (!Window.IsDestroyed)
bool isDestroyed = (bool)typeof(Window).GetProperty("IsDestroyed", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic).GetValue(Window);
if (!isDestroyed)
_window.Destroying();
}
else
Expand All @@ -87,7 +88,8 @@ void OnActivated(object sender, UI.Xaml.WindowActivatedEventArgs args)
{
if (Window is not null)
{
if (!Window.IsActivated)
bool isActivated = (bool)typeof(Window).GetProperty("IsActivated", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic).GetValue(Window);
if (!isActivated)
_window.Activated();
}
else
Expand Down
28 changes: 23 additions & 5 deletions src/Core/src/MauiContextExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
using Microsoft.Maui.Devices;
using Microsoft.Maui.Hosting;
using Microsoft.Maui.ApplicationModel;
using System;


#if WINDOWS
using NativeApplication = Microsoft.UI.Xaml.Application;
Expand Down Expand Up @@ -32,14 +34,30 @@ public static IAnimationManager GetAnimationManager(this IMauiContext mauiContex
public static IDispatcher GetDispatcher(this IMauiContext mauiContext) =>
mauiContext.Services.GetRequiredService<IDispatcher>();

public static IDispatcher? GetOptionalDispatcher(this IMauiContext mauiContext) =>
public static IDispatcher GetOptionalDispatcher(this IMauiContext mauiContext) =>
mauiContext.Services.GetService<IDispatcher>();

static void AddWeakSpecific<TService>(IServiceProvider services, TService instance)
where TService : class
{
var type = services.GetType();
var addSpecific = type.GetMethod("AddSpecific", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance);
addSpecific.Invoke(services, new object[] { typeof(TService), static (object state) => (object)((WeakReference)state).Target, new WeakReference(instance) });
}

static void AddSpecific<TService>(IServiceProvider services, TService instance)
where TService : class
{
var type = services.GetType();
var addSpecific = type.GetMethod("AddSpecific", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance);
addSpecific.Invoke(services, new object[] { typeof(TService), static (object state) => (object)state, instance });
}

public static IMauiContext MakeApplicationScope(this IMauiContext mauiContext, NativeApplication platformApplication)
{
var scopedContext = new MauiContext(mauiContext.Services);

scopedContext.AddSpecific(platformApplication);
AddSpecific(scopedContext.Services, platformApplication);

return scopedContext;
}
Expand All @@ -56,13 +74,13 @@ public static IMauiContext MakeWindowScope(this IMauiContext mauiContext, Native
var scopedContext = new MauiContext(scope.ServiceProvider);
#endif

scopedContext.AddWeakSpecific(platformWindow);
AddWeakSpecific(scopedContext.Services, platformWindow);

#if ANDROID
scopedContext.AddSpecific(new NavigationRootManager(scopedContext));
AddSpecific(scopedContext.Services, new NavigationRootManager(scopedContext));
#endif
#if WINDOWS
scopedContext.AddSpecific(new NavigationRootManager(platformWindow));
AddSpecific(scopedContext.Services, new NavigationRootManager(platformWindow));
#endif

// Initialize any window-scoped services, for example the window dispatchers and animation tickers
Expand Down
3 changes: 3 additions & 0 deletions src/Core/src/Platform/ElementExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,9 @@ static void SetHandler(this BasePlatformType nativeElement, IElement element, IM
handler.SetVirtualView(element);
}

public static void SetApplicationHandler2(this PlatformApplication platformApplication, IApplication application, IMauiContext context) =>
SetHandler(platformApplication, application, context);

public static void SetApplicationHandler(this PlatformApplication platformApplication, IApplication application, IMauiContext context) =>
SetHandler(platformApplication, application, context);

Expand Down
3 changes: 3 additions & 0 deletions src/Core/src/Platform/Windows/FrameworkElementExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#nullable enable
#if WINDOWS
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
Expand All @@ -15,6 +16,7 @@
using WBindingExpression = Microsoft.UI.Xaml.Data.BindingExpression;
using WBrush = Microsoft.UI.Xaml.Media.Brush;
using WPoint = Windows.Foundation.Point;
using ActionDisposable = Microsoft.Maui.DeviceTests.ActionDisposable;

namespace Microsoft.Maui.Platform
{
Expand Down Expand Up @@ -403,3 +405,4 @@ internal static bool TryGetInputPane([NotNullWhen(true)] out InputPane? inputPan
}
}
}
#endif
8 changes: 8 additions & 0 deletions src/Core/tests/DeviceTests.Shared/MauiProgramDefaults.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
using System.Linq;
using System.Reflection;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Maui.Controls.Hosting;
using Microsoft.Maui.Dispatching;
using Microsoft.Maui.Hosting;
using Microsoft.Maui.LifecycleEvents;
using Microsoft.Maui.TestUtils.DeviceTests.Runners;
Expand Down Expand Up @@ -75,6 +77,12 @@ public static MauiApp CreateMauiApp(List<Assembly> testAssemblies)
ValidateScopes = true,
}));

appBuilder.ConfigureDispatching();

// Running from VisualStudio on Windows attaches surface tap for hot reload and requires IDispatcher,
// either our setup has a service mismatch in the Utils.DeivceTests, or there is a way to run without hot reload on Windows.
appBuilder.Services.AddSingleton<IDispatcher>(service => service.GetRequiredService<IDispatcherProvider>().GetForCurrentThread());

var mauiApp = appBuilder.Build();

DefaultTestApp = mauiApp.Services.GetRequiredService<IApplication>();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>$(MauiDeviceTestsPlatforms)</TargetFrameworks>
<TargetFrameworks>net8.0-ios;net8.0-android;net8.0-maccatalyst</TargetFrameworks>
<TargetFrameworks Condition="$([MSBuild]::IsOSPlatform('windows'))">$(TargetFrameworks);net8.0-windows10.0.19041.0</TargetFrameworks>
<SingleProject>true</SingleProject>
<RootNamespace>Microsoft.Maui.TestUtils.DeviceTests.Runners</RootNamespace>
<AssemblyName>Microsoft.Maui.TestUtils.DeviceTests.Runners</AssemblyName>
Expand Down
Loading

0 comments on commit a637884

Please sign in to comment.