Files
av-parser/tests/AvParser.UI.HeadlessTests/LocalizationViewTests.cs
T
Leonid PershinandClaude Opus 5 f8744c930a Remove the demo text-parsing domain
The scaffolding domain existed to prove the shell end to end before there was
anything real to put in it. There is now, so it goes - as CLAUDE.md promised it
would.

Gone: the two sample parsers, ITextParser, ParsedRecord, the parser catalog,
ParseViewModel and ParseView, their tests, and the settings key that remembered
which parser was last used. ParseError.LineNumber becomes Index, since for a
listing "line 42" was simply untrue, and the error keys move from Parse.Error.*
to Collect.Error.* now that parsing is not a concept here.

Kept: IParser<,>, ParseOutcome, ParseProgress and ParseError. The streaming
contract was always the general part - it was only ever the text-shaped closure
of it that was scaffolding.

Rendering the dashboard caught two keys that were referenced but never added
during the rename: the XAML was repointed and the resources were not. The parity
test could not see it, because it compares the two files against each other and
a key absent from both is consistent. That gap now has its own test, which reads
every {l:Loc} in the XAML and checks it resolves - a screenshot is too late and
too manual a way to find a missing string.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-13 22:23:52 +03:00

91 lines
3.1 KiB
C#

using Avalonia;
using Avalonia.Controls;
using Avalonia.Headless.XUnit;
using Avalonia.Threading;
using Avalonia.VisualTree;
using AvParser.Core.Settings;
using AvParser.UI.Localization;
using AvParser.UI.Navigation;
using AvParser.UI.ViewModels;
using AvParser.UI.Views;
using ReactiveUI.Primitives.Concurrency;
namespace AvParser.UI.HeadlessTests;
public sealed class LocalizationViewTests : IDisposable
{
/// <summary>Leaves the localizer where the rest of the assembly expects it.</summary>
public void Dispose() => Localizer.Instance.SetLanguage(AppLanguage.English);
private static (ShellView View, ShellViewModel ViewModel) ShowShell()
{
var navigation = new NavigationService([new FakePage("Page.Dashboard"), new FakePage("Page.Collect")]);
var viewModel = new ShellViewModel(navigation, new FakeThemeService(), ImmediateSequencer.Instance);
var view = new ShellView { DataContext = viewModel };
new Window
{
Width = 1400,
Height = 800,
Content = view,
}.Show();
Dispatcher.UIThread.RunJobs();
return (view, viewModel);
}
private static IReadOnlyList<string> RailLabels(Visual view) =>
[
.. view.GetVisualDescendants()
.OfType<TextBlock>()
.Where(block => block.Classes.Contains("navLabel"))
.Select(block => block.Text ?? string.Empty),
];
[AvaloniaFact]
public void Rendered_text_follows_the_language_without_rebuilding_the_view()
{
Localizer.Instance.SetLanguage(AppLanguage.English);
var (view, _) = ShowShell();
var english = RailLabels(view);
english.ShouldBe(["Dashboard", "Collect"]);
Localizer.Instance.SetLanguage(AppLanguage.Russian);
Dispatcher.UIThread.RunJobs();
// The same controls, not a rebuilt tree: this is the whole point of binding through the
// localizer's indexer rather than resolving strings once at load.
RailLabels(view).ShouldBe(["Обзор", "Сбор"]);
}
[AvaloniaFact]
public void The_title_bar_follows_the_language_too()
{
Localizer.Instance.SetLanguage(AppLanguage.English);
var (view, viewModel) = ShowShell();
var title = view.GetVisualDescendants().OfType<TextBlock>().Single(block => block.Name == "ShellTitle");
title.Text.ShouldBe("Dashboard");
Localizer.Instance.SetLanguage(AppLanguage.Russian);
Dispatcher.UIThread.RunJobs();
viewModel.Title.ShouldBe("Обзор");
title.Text.ShouldBe("Обзор");
}
[AvaloniaFact]
public void Loc_markup_resolves_a_static_caption()
{
Localizer.Instance.SetLanguage(AppLanguage.Russian);
var (view, _) = ShowShell();
// Tooltips come from {l:Loc} rather than from a view model, so they exercise the markup
// extension itself.
var toggle = view.GetVisualDescendants().OfType<Button>().Single(button => button.Name == "PaneToggle");
ToolTip.GetTip(toggle).ShouldBe("Показать или скрыть навигацию");
}
}