using System;
using System.Collections.Generic;
using NUnit.Framework;
using StardewModdingAPI.Framework;
using StardewValley;
namespace StardewModdingAPI.Tests
{
/// Unit tests for and .
[TestFixture]
public class TranslationTests
{
/*********
** Data
*********/
/// Sample translation text for unit tests.
public static string[] Samples = { null, "", " ", "boop", " boop " };
/// A token structure type.
public enum TokenType
{
/// The tokens are passed in a dictionary.
Dictionary,
/// The tokens are passed in an anonymous object.
AnonymousObject
}
/*********
** Unit tests
*********/
/****
** Translation helper
****/
[Test(Description = "Assert that the translation helper correctly handles no translations.")]
public void Helper_HandlesNoTranslations()
{
// arrange
var data = new Dictionary>();
// act
ITranslationHelper helper = new TranslationHelper("ModName", "en", LocalizedContentManager.LanguageCode.en).SetTranslations(data);
Translation translation = helper.Get("key");
// assert
Assert.AreEqual("en", helper.Locale, "The locale doesn't match the input value.");
Assert.AreEqual(LocalizedContentManager.LanguageCode.en, helper.LocaleEnum, "The locale enum doesn't match the input value.");
Assert.IsNotNull(helper.GetTranslations(), "The full list of translations is unexpectedly null.");
Assert.AreEqual(0, helper.GetTranslations().Count, "The full list of translations is unexpectedly not empty.");
Assert.IsNotNull(translation, "The translation helper unexpectedly returned a null translation.");
Assert.AreEqual(this.GetPlaceholderText("key"), translation.ToString(), "The translation returned an unexpected value.");
}
[Test(Description = "Assert that the translation helper returns the expected translations correctly.")]
public void Helper_GetTranslations_ReturnsExpectedText()
{
// arrange
var data = this.GetSampleData();
var expected = this.GetExpectedTranslations();
// act
var actual = new Dictionary>();
TranslationHelper helper = new TranslationHelper("ModName", "en", LocalizedContentManager.LanguageCode.en).SetTranslations(data);
foreach (string locale in expected.Keys)
{
this.AssertSetLocale(helper, locale, LocalizedContentManager.LanguageCode.en);
actual[locale] = helper.GetTranslations();
}
// assert
foreach (string locale in expected.Keys)
{
Assert.IsNotNull(actual[locale], $"The translations for {locale} is unexpectedly null.");
Assert.That(actual[locale], Is.EquivalentTo(expected[locale]), $"The translations for {locale} don't match the expected values.");
}
}
[Test(Description = "Assert that the translations returned by the helper has the expected text.")]
public void Helper_Get_ReturnsExpectedText()
{
// arrange
var data = this.GetSampleData();
var expected = this.GetExpectedTranslations();
// act
var actual = new Dictionary>();
TranslationHelper helper = new TranslationHelper("ModName", "en", LocalizedContentManager.LanguageCode.en).SetTranslations(data);
foreach (string locale in expected.Keys)
{
this.AssertSetLocale(helper, locale, LocalizedContentManager.LanguageCode.en);
actual[locale] = new Dictionary();
foreach (string key in expected[locale].Keys)
actual[locale][key] = helper.Get(key);
}
// assert
foreach (string locale in expected.Keys)
{
Assert.IsNotNull(actual[locale], $"The translations for {locale} is unexpectedly null.");
Assert.That(actual[locale], Is.EquivalentTo(expected[locale]), $"The translations for {locale} don't match the expected values.");
}
}
/****
** Translation
****/
[Test(Description = "Assert that HasValue returns the expected result for various inputs.")]
[TestCase(null, ExpectedResult = false)]
[TestCase("", ExpectedResult = false)]
[TestCase(" ", ExpectedResult = true)]
[TestCase("boop", ExpectedResult = true)]
[TestCase(" boop ", ExpectedResult = true)]
public bool Translation_HasValue(string text)
{
return new Translation("ModName", "pt-BR", "key", text).HasValue();
}
[Test(Description = "Assert that the translation's ToString method returns the expected text for various inputs.")]
public void Translation_ToString([ValueSource(nameof(TranslationTests.Samples))] string text)
{
// act
Translation translation = new Translation("ModName", "pt-BR", "key", text);
// assert
if (translation.HasValue())
Assert.AreEqual(text, translation.ToString(), "The translation returned an unexpected value given a valid input.");
else
Assert.AreEqual(this.GetPlaceholderText("key"), translation.ToString(), "The translation returned an unexpected value given a null or empty input.");
}
[Test(Description = "Assert that the translation's implicit string conversion returns the expected text for various inputs.")]
public void Translation_ImplicitStringConversion([ValueSource(nameof(TranslationTests.Samples))] string text)
{
// act
Translation translation = new Translation("ModName", "pt-BR", "key", text);
// assert
if (translation.HasValue())
Assert.AreEqual(text, (string)translation, "The translation returned an unexpected value given a valid input.");
else
Assert.AreEqual(this.GetPlaceholderText("key"), (string)translation, "The translation returned an unexpected value given a null or empty input.");
}
[Test(Description = "Assert that the translation returns the expected text given a use-placeholder setting.")]
public void Translation_UsePlaceholder([Values(true, false)] bool value, [ValueSource(nameof(TranslationTests.Samples))] string text)
{
// act
Translation translation = new Translation("ModName", "pt-BR", "key", text).UsePlaceholder(value);
// assert
if (translation.HasValue())
Assert.AreEqual(text, translation.ToString(), "The translation returned an unexpected value given a valid input.");
else if (!value)
Assert.AreEqual(text, translation.ToString(), "The translation returned an unexpected value given a null or empty input with the placeholder disabled.");
else
Assert.AreEqual(this.GetPlaceholderText("key"), translation.ToString(), "The translation returned an unexpected value given a null or empty input with the placeholder enabled.");
}
[Test(Description = "Assert that the translation's Assert method throws the expected exception.")]
public void Translation_Assert([ValueSource(nameof(TranslationTests.Samples))] string text)
{
// act
Translation translation = new Translation("ModName", "pt-BR", "key", text);
// assert
if (translation.HasValue())
Assert.That(() => translation.Assert(), Throws.Nothing, "The assert unexpected threw an exception for a valid input.");
else
Assert.That(() => translation.Assert(), Throws.Exception.TypeOf(), "The assert didn't throw an exception for invalid input.");
}
[Test(Description = "Assert that the translation returns the expected text after setting the default.")]
public void Translation_Default([ValueSource(nameof(TranslationTests.Samples))] string text, [ValueSource(nameof(TranslationTests.Samples))] string @default)
{
// act
Translation translation = new Translation("ModName", "pt-BR", "key", text).Default(@default);
// assert
if (!string.IsNullOrEmpty(text))
Assert.AreEqual(text, translation.ToString(), "The translation returned an unexpected value given a valid base text.");
else if (!string.IsNullOrEmpty(@default))
Assert.AreEqual(@default, translation.ToString(), "The translation returned an unexpected value given a null or empty base text, but valid default.");
else
Assert.AreEqual(this.GetPlaceholderText("key"), translation.ToString(), "The translation returned an unexpected value given a null or empty base and default text.");
}
/****
** Translation tokens
****/
[Test(Description = "Assert that multiple translation tokens are replaced correctly regardless of the token structure.")]
public void Translation_Tokens([Values(TokenType.AnonymousObject, TokenType.Dictionary)] TokenType tokenType)
{
// arrange
string start = Guid.NewGuid().ToString("N");
string middle = Guid.NewGuid().ToString("N");
string end = Guid.NewGuid().ToString("N");
const string input = "{{start}} tokens are properly replaced (including {{middle}} {{ MIDdlE}}) {{end}}";
string expected = $"{start} tokens are properly replaced (including {middle} {middle}) {end}";
// act
Translation translation = new Translation("ModName", "pt-BR", "key", input);
switch (tokenType)
{
case TokenType.AnonymousObject:
translation = translation.Tokens(new { start, middle, end });
break;
case TokenType.Dictionary:
translation = translation.Tokens(new Dictionary { ["start"] = start, ["middle"] = middle, ["end"] = end });
break;
default:
throw new NotSupportedException($"Unknown token type {tokenType}.");
}
// assert
Assert.AreEqual(expected, translation.ToString(), "The translation returned an unexpected text.");
}
[Test(Description = "Assert that the translation can replace tokens in all valid formats.")]
[TestCase("{{value}}", "value")]
[TestCase("{{ value }}", "value")]
[TestCase("{{value }}", "value")]
[TestCase("{{ the_value }}", "the_value")]
[TestCase("{{ the.value_here }}", "the.value_here")]
[TestCase("{{ the_value-here.... }}", "the_value-here....")]
[TestCase("{{ tHe_vALuE-HEre.... }}", "tHe_vALuE-HEre....")]
public void Translation_Tokens_ValidFormats(string text, string key)
{
// arrange
string value = Guid.NewGuid().ToString("N");
// act
Translation translation = new Translation("ModName", "pt-BR", "key", text).Tokens(new Dictionary { [key] = value });
// assert
Assert.AreEqual(value, translation.ToString(), "The translation returned an unexpected value given a valid base text.");
}
[Test(Description = "Assert that translation tokens are case-insensitive and surrounding-whitespace-insensitive.")]
[TestCase("{{value}}", "value")]
[TestCase("{{VaLuE}}", "vAlUe")]
[TestCase("{{VaLuE }}", " vAlUe")]
public void Translation_Tokens_KeysAreNormalised(string text, string key)
{
// arrange
string value = Guid.NewGuid().ToString("N");
// act
Translation translation = new Translation("ModName", "pt-BR", "key", text).Tokens(new Dictionary { [key] = value });
// assert
Assert.AreEqual(value, translation.ToString(), "The translation returned an unexpected value given a valid base text.");
}
/*********
** Private methods
*********/
/// Set a translation helper's locale and assert that it was set correctly.
/// The translation helper to change.
/// The expected locale.
/// The expected game language code.
private void AssertSetLocale(TranslationHelper helper, string locale, LocalizedContentManager.LanguageCode localeEnum)
{
helper.SetLocale(locale, localeEnum);
Assert.AreEqual(locale, helper.Locale, "The locale doesn't match the input value.");
Assert.AreEqual(localeEnum, helper.LocaleEnum, "The locale enum doesn't match the input value.");
}
/// Get sample raw translations to input.
private IDictionary> GetSampleData()
{
return new Dictionary>
{
["default"] = new Dictionary
{
["key A"] = "default A",
["key C"] = "default C"
},
["en"] = new Dictionary
{
["key A"] = "en A",
["key B"] = "en B"
},
["en-US"] = new Dictionary(),
["zzz"] = new Dictionary
{
["key A"] = "zzz A"
}
};
}
/// Get the expected translation output given , based on the expected locale fallback.
private IDictionary> GetExpectedTranslations()
{
return new Dictionary>
{
["default"] = new Dictionary
{
["key A"] = "default A",
["key C"] = "default C"
},
["en"] = new Dictionary
{
["key A"] = "en A",
["key B"] = "en B",
["key C"] = "default C"
},
["en-us"] = new Dictionary
{
["key A"] = "en A",
["key B"] = "en B",
["key C"] = "default C"
},
["zzz"] = new Dictionary
{
["key A"] = "zzz A",
["key C"] = "default C"
}
};
}
/// Get the default placeholder text when a translation is missing.
/// The translation key.
private string GetPlaceholderText(string key)
{
return string.Format(Translation.PlaceholderText, key);
}
}
}