added tests to project
This commit is contained in:
@@ -0,0 +1,178 @@
|
||||
using System.Collections;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Resources;
|
||||
using System.Text.RegularExpressions;
|
||||
using CursorLang.Models;
|
||||
|
||||
namespace CursorLang.Tests.Resources;
|
||||
|
||||
/// <summary>
|
||||
/// Checks of the resources themselves: they carry every caption in the settings
|
||||
/// window, and a missing key only shows on a live window.
|
||||
/// </summary>
|
||||
public sealed partial class StringsTests
|
||||
{
|
||||
private static readonly ResourceManager Resources =
|
||||
new("CursorLang.Resources.Strings", typeof(App).Assembly);
|
||||
|
||||
private static readonly CultureInfo English = CultureInfo.GetCultureInfo("en");
|
||||
private static readonly CultureInfo Russian = CultureInfo.GetCultureInfo("ru");
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(EnumKeys))]
|
||||
public void Every_list_value_has_an_English_caption(string key)
|
||||
{
|
||||
Assert.False(string.IsNullOrWhiteSpace(Resources.GetString(key, English)));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(EnumKeys))]
|
||||
public void Every_list_value_has_a_Russian_caption(string key)
|
||||
{
|
||||
Assert.False(string.IsNullOrWhiteSpace(Resources.GetString(key, Russian)));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void The_Russian_translation_covers_every_string()
|
||||
{
|
||||
List<string> missing = [];
|
||||
|
||||
foreach (string key in NeutralKeys())
|
||||
{
|
||||
// An untranslated resource falls back to English, so the Russian set
|
||||
// is asked directly rather than through the string with a fallback
|
||||
if (RussianSet().GetString(key) is null)
|
||||
{
|
||||
missing.Add(key);
|
||||
}
|
||||
}
|
||||
|
||||
Assert.Empty(missing);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void The_Russian_translation_has_no_extra_strings()
|
||||
{
|
||||
HashSet<string> neutral = [.. NeutralKeys()];
|
||||
List<string> extra = [];
|
||||
|
||||
foreach (DictionaryEntry entry in RussianSet())
|
||||
{
|
||||
var key = (string)entry.Key;
|
||||
if (!neutral.Contains(key))
|
||||
{
|
||||
extra.Add(key);
|
||||
}
|
||||
}
|
||||
|
||||
Assert.Empty(extra);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void There_are_no_empty_strings_in_the_resources()
|
||||
{
|
||||
foreach (string key in NeutralKeys())
|
||||
{
|
||||
Assert.False(string.IsNullOrWhiteSpace(Resources.GetString(key, English)), key);
|
||||
Assert.False(string.IsNullOrWhiteSpace(Resources.GetString(key, Russian)), key);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Every key the settings window markup asks for has to exist in the
|
||||
/// resources: otherwise the user sees the key itself in its place.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Every_key_from_the_settings_window_markup_exists_in_the_resources()
|
||||
{
|
||||
HashSet<string> known = [.. NeutralKeys()];
|
||||
List<string> missing = [];
|
||||
|
||||
foreach (Match match in LocalizationBinding().Matches(ReadSettingsWindowMarkup()))
|
||||
{
|
||||
string key = match.Groups["key"].Value;
|
||||
if (!known.Contains(key))
|
||||
{
|
||||
missing.Add(key);
|
||||
}
|
||||
}
|
||||
|
||||
Assert.Empty(missing);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The version of an update is put into the string by the app, so the place
|
||||
/// for it has to be there in both languages.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void The_string_about_an_available_update_has_room_for_the_version()
|
||||
{
|
||||
Assert.Contains("{0}", Resources.GetString("UpdateAvailable", English), StringComparison.Ordinal);
|
||||
Assert.Contains("{0}", Resources.GetString("UpdateAvailable", Russian), StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
/// <summary>The markup does ask for strings — otherwise the check above means nothing.</summary>
|
||||
[Fact]
|
||||
public void The_settings_window_markup_asks_for_resource_strings()
|
||||
{
|
||||
Assert.NotEmpty(LocalizationBinding().Matches(ReadSettingsWindowMarkup()));
|
||||
}
|
||||
|
||||
public static TheoryData<string> EnumKeys()
|
||||
{
|
||||
var data = new TheoryData<string>();
|
||||
|
||||
foreach (string key in EnumKeysOf<AppTheme>())
|
||||
{
|
||||
data.Add(key);
|
||||
}
|
||||
|
||||
foreach (string key in EnumKeysOf<PopupPlacementMode>())
|
||||
{
|
||||
data.Add(key);
|
||||
}
|
||||
|
||||
foreach (string key in EnumKeysOf<AnchorSide>())
|
||||
{
|
||||
data.Add(key);
|
||||
}
|
||||
|
||||
foreach (string key in EnumKeysOf<ScreenPosition>())
|
||||
{
|
||||
data.Add(key);
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
// A caption key is built from the type name and the value: PopupPlacementMode_AtCursor
|
||||
private static IEnumerable<string> EnumKeysOf<TEnum>() where TEnum : struct, Enum =>
|
||||
Enum.GetValues<TEnum>().Select(value => $"{typeof(TEnum).Name}_{value}");
|
||||
|
||||
private static IEnumerable<string> NeutralKeys()
|
||||
{
|
||||
ResourceSet set = Resources.GetResourceSet(CultureInfo.InvariantCulture, true, true)!;
|
||||
|
||||
foreach (DictionaryEntry entry in set)
|
||||
{
|
||||
yield return (string)entry.Key;
|
||||
}
|
||||
}
|
||||
|
||||
private static ResourceSet RussianSet() =>
|
||||
Resources.GetResourceSet(Russian, createIfNotExists: true, tryParents: false)!;
|
||||
|
||||
private static string ReadSettingsWindowMarkup()
|
||||
{
|
||||
using Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("MainWindow.xaml")
|
||||
?? throw new InvalidOperationException("The settings window markup is not embedded in the test assembly");
|
||||
|
||||
using var reader = new StreamReader(stream);
|
||||
return reader.ReadToEnd();
|
||||
}
|
||||
|
||||
[GeneratedRegex(@"Localization\[(?<key>\w+)\]")]
|
||||
private static partial Regex LocalizationBinding();
|
||||
}
|
||||
Reference in New Issue
Block a user