Files

241 lines
8.5 KiB
C#

using AtomUI.Controls.Localization;
using AtomUI.Localization;
using AtomUI.Theme;
using AtomUI.Theme.Configuration;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
using Avalonia.Markup.Xaml.MarkupExtensions;
using Avalonia.Threading;
using Avalonia.VisualTree;
using Shouldly;
using Xunit;
using AtomUIDialog = AtomUI.Desktop.Controls.Dialog;
using AtomUIFlyout = AtomUI.Desktop.Controls.Flyout;
using AtomUIPopup = AtomUI.Desktop.Controls.Popup;
using AtomUIWindow = AtomUI.Desktop.Controls.Window;
using AvaloniaWindow = Avalonia.Controls.Window;
namespace AtomUI.Desktop.Controls.Tests.Localization;
public class CommonCatalogTests
{
static CommonCatalogTests()
{
AvaloniaTestApp.EnsureInitialized();
}
[Fact]
public void Common_Catalog_Uses_Stable_Keys_For_All_Existing_Entries()
{
var expectedKeys = new[]
{
nameof(CommonLangResourceKind.Ok),
nameof(CommonLangResourceKind.Submit),
nameof(CommonLangResourceKind.Cancel),
nameof(CommonLangResourceKind.Reset),
nameof(CommonLangResourceKind.Edit),
nameof(CommonLangResourceKind.Delete),
nameof(CommonLangResourceKind.Save),
nameof(CommonLangResourceKind.NoData),
nameof(CommonLangResourceKind.Loading),
nameof(CommonLangResourceKind.Optional)
};
Enum.GetNames<CommonLangResourceKind>().ShouldBe(expectedKeys);
}
[Fact]
public void Common_Markup_Extension_Uses_The_New_Localization_Resource_Base()
{
typeof(CommonLangResourceExtension).IsSealed.ShouldBeTrue();
typeof(CommonLangResourceExtension).BaseType.ShouldBe(
typeof(LanguageResourceExtension<CommonLangResourceKind>));
typeof(CommonLangResourceExtension).GetConstructor(Type.EmptyTypes).ShouldNotBeNull();
typeof(CommonLangResourceExtension).GetConstructor([typeof(CommonLangResourceKind)]).ShouldNotBeNull();
}
[Fact]
public void UseDesktopControls_Registers_All_BuiltIn_Common_Translations()
{
var application = Application.Current.ShouldNotBeNull();
var languageManager = application.GetLanguageManager().ShouldNotBeNull();
var localizer = application.GetLocalizer().ShouldNotBeNull();
var text = new TextBlock();
var extension = new CommonLangResourceExtension(CommonLangResourceKind.Ok);
var dynamicResource = extension.ProvideValue(
new ProvideValueServiceProvider(text, TextBlock.TextProperty))
.ShouldBeOfType<DynamicResourceExtension>();
dynamicResource.ResourceKey.ShouldBe(CommonLangResourceKind.Ok);
text.Bind(TextBlock.TextProperty, dynamicResource);
var window = new AvaloniaWindow
{
Content = text
};
window.Show();
try
{
text.Text.ShouldBe("Ok");
localizer.Get(CommonLangResourceKind.Ok).ShouldBe("Ok");
languageManager.ChangeLanguage(LanguageTags.ZhCN);
text.Text.ShouldBe("确定");
localizer.Get(CommonLangResourceKind.Loading).ShouldBe("正在加载数据");
languageManager.ChangeLanguage(LanguageTags.ZhTW);
text.Text.ShouldBe("確定");
localizer.Get(CommonLangResourceKind.Optional).ShouldBe("(可選)");
}
finally
{
window.Close();
}
}
[Fact]
public void Common_Catalog_No_Longer_Contains_Legacy_Language_Providers()
{
var controlsAssembly = typeof(CommonLangResourceKind).Assembly;
controlsAssembly.GetType("AtomUI.Controls.Localization.en_US").ShouldBeNull();
controlsAssembly.GetType("AtomUI.Controls.Localization.zh_CN").ShouldBeNull();
controlsAssembly.GetType("AtomUI.Controls.Localization.zh_TW").ShouldBeNull();
}
[Fact]
public void Application_Language_State_Reaches_Window_Dialog_Popup_Flyout_And_NonVisual_Consumers()
{
var application = Application.Current.ShouldNotBeNull();
var languageManager = application.GetLanguageManager().ShouldNotBeNull();
var localizer = application.GetLocalizer().ShouldNotBeNull();
languageManager.ChangeLanguage(LanguageTags.EnUS);
var windowText = CreateLocalizedText();
var dialogText = CreateLocalizedText();
var popupText = CreateLocalizedText();
var flyoutText = CreateLocalizedText();
var placementTarget = new Border
{
Width = 80,
Height = 24
};
var popup = new AtomUIPopup
{
PlacementTarget = placementTarget,
ShouldUseOverlayLayer = true,
Child = popupText
};
var canvas = new Canvas();
canvas.Children.Add(windowText);
canvas.Children.Add(placementTarget);
canvas.Children.Add(popup);
var themeScope = new ThemeConfigProvider
{
Config = new ThemeConfigBuilder().Build(),
Child = canvas
};
var window = new AtomUIWindow
{
Width = 320,
Height = 240,
Content = themeScope
};
var dialog = new AtomUIDialog
{
PlacementTarget = placementTarget,
Content = dialogText,
StandardButtons = DialogStandardButton.NoButton,
IsMotionEnabled = false
};
var flyout = new AtomUIFlyout
{
Content = flyoutText
};
Task? dialogTask = null;
try
{
window.Show();
Dispatcher.UIThread.RunJobs();
popup.IsOpen = true;
flyout.ShowAt(placementTarget);
dialogTask = dialog.OpenAsync(TestContext.Current.CancellationToken);
PumpUntil(() =>
TopLevel.GetTopLevel(dialogText) is not null &&
TopLevel.GetTopLevel(popupText) is not null &&
TopLevel.GetTopLevel(flyoutText) is not null);
AssertLocalizedText("Ok", windowText, dialogText, popupText, flyoutText);
ResolveForPlainObject().ShouldBe("Ok");
localizer.Get(CommonLangResourceKind.Optional).ShouldBe("(optional)");
languageManager.ChangeLanguage(LanguageTags.ZhTW);
Dispatcher.UIThread.RunJobs();
AssertLocalizedText("確定", windowText, dialogText, popupText, flyoutText);
ResolveForPlainObject().ShouldBe("確定");
localizer.Get(CommonLangResourceKind.Optional).ShouldBe("(可選)");
}
finally
{
if (dialogTask is not null && !dialogTask.IsCompleted)
{
dialog.Reject();
PumpUntil(() => dialogTask.IsCompleted);
}
flyout.Hide();
popup.IsOpen = false;
window.Close();
languageManager.ChangeLanguage(LanguageTags.EnUS);
}
}
private static TextBlock CreateLocalizedText()
{
var text = new TextBlock();
var extension = new CommonLangResourceExtension(CommonLangResourceKind.Ok);
var dynamicResource = extension.ProvideValue(
new ProvideValueServiceProvider(text, TextBlock.TextProperty))
.ShouldBeOfType<DynamicResourceExtension>();
text.Bind(TextBlock.TextProperty, dynamicResource);
return text;
}
private static string ResolveForPlainObject()
{
var extension = new CommonLangResourceExtension(CommonLangResourceKind.Ok);
return extension.ProvideValue(new ProvideValueServiceProvider(new object(), new object()))
.ShouldBeOfType<string>();
}
private static void AssertLocalizedText(string expected, params TextBlock[] consumers)
{
consumers.ShouldAllBe(consumer => consumer.Text == expected);
}
private static void PumpUntil(Func<bool> condition)
{
var timeoutAt = DateTimeOffset.UtcNow + TimeSpan.FromSeconds(5);
while (!condition() && DateTimeOffset.UtcNow < timeoutAt)
{
Dispatcher.UIThread.RunJobs();
Thread.Sleep(1);
}
condition().ShouldBeTrue();
}
private sealed class ProvideValueServiceProvider(
object targetObject,
object targetProperty) : IServiceProvider, IProvideValueTarget
{
public object TargetObject { get; } = targetObject;
public object TargetProperty { get; } = targetProperty;
public object? GetService(Type serviceType)
{
return serviceType == typeof(IProvideValueTarget) ? this : null;
}
}
}