Files
av-parser/tests/AvParser.UI.Tests/LocalizationCoverageTests.cs
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

66 lines
2.2 KiB
C#

using System.Reflection;
using System.Resources;
using System.Text.RegularExpressions;
using AvParser.UI.Localization;
namespace AvParser.UI.Tests;
/// <summary>
/// Every key the XAML asks for must actually exist.
/// </summary>
/// <remarks>
/// The parity test compares the two resource files against each other, so a key missing from
/// <b>both</b> passes it happily and then renders as <c>!Some.Key!</c> in the running app. That
/// happened during a rename: the XAML was repointed at new key names and the keys themselves were
/// never added. Only a screenshot caught it, which is too late and too manual.
/// </remarks>
public sealed partial class LocalizationCoverageTests
{
[GeneratedRegex(@"\{l:Loc\s+([A-Za-z0-9_.\-]+)\s*\}", RegexOptions.Compiled)]
private static partial Regex LocMarkup();
private static readonly ResourceManager Resources = new(
"AvParser.UI.Localization.Strings",
typeof(Localizer).GetTypeInfo().Assembly
);
/// <summary>Walks up to the repository root, which the test binary sits several levels below.</summary>
private static DirectoryInfo RepositoryRoot()
{
var directory = new DirectoryInfo(AppContext.BaseDirectory);
while (directory is not null && !File.Exists(Path.Combine(directory.FullName, "AvParser.slnx")))
{
directory = directory.Parent;
}
return directory ?? throw new InvalidOperationException("Could not find the repository root.");
}
[Fact]
public void Every_key_used_in_xaml_exists()
{
var views = Path.Combine(RepositoryRoot().FullName, "src", "AvParser.UI");
var files = Directory.GetFiles(views, "*.axaml", SearchOption.AllDirectories);
files.ShouldNotBeEmpty();
var missing = new SortedSet<string>(StringComparer.Ordinal);
foreach (var file in files)
{
foreach (Match match in LocMarkup().Matches(File.ReadAllText(file)))
{
var key = match.Groups[1].Value;
if (Resources.GetString(key, Localizer.Instance.Culture) is null)
{
missing.Add($"{key} ({Path.GetFileName(file)})");
}
}
}
missing.ShouldBeEmpty();
}
}