using AtomUI.Build.Tasks.LocalizationBuild; using Shouldly; using Xunit; namespace AtomUI.Build.Tasks.Tests; public class LanguageFileValidationTests { [Fact] public void ValidateTarget_Reports_Stale_Source_And_Missing_Complete_Unit() { var source = Parse(SourceXliff( ("Title", "Hello"), ("Body", "Body"))); var target = Parse(TargetXliff( "ja-JP", ("Title", "Changed", "こんにちは", "final"))); var diagnostics = LanguageFileValidation.ValidateTarget( source, target, new LanguageFileValidationOptions(requireCompleteBundle: true, minimumTargetState: "final")); diagnostics.ShouldContain(item => item.Kind == LanguageFileValidationDiagnosticKind.CatalogMismatch && item.Message.Contains("Title", StringComparison.Ordinal)); diagnostics.ShouldContain(item => item.Kind == LanguageFileValidationDiagnosticKind.InvalidTranslation && item.Message.Contains("Body", StringComparison.Ordinal)); } [Fact] public void ValidateTarget_Allows_Partial_Override_But_Still_Validates_Present_Units() { var source = Parse(SourceXliff( ("Title", "Hello"), ("Body", "Body"))); var target = Parse(TargetXliff( "ja-JP", ("Title", "Hello", "こんにちは", "final"))); var diagnostics = LanguageFileValidation.ValidateTarget( source, target, new LanguageFileValidationOptions(requireCompleteBundle: false, minimumTargetState: "final")); diagnostics.ShouldBeEmpty(); } [Fact] public void ValidateTarget_Reports_Unpublishable_Target_With_Actionable_Message() { var source = Parse(SourceXliff(("Title", "Hello"))); var target = Parse(TargetXliff( "ja-JP", ("Title", "Hello", " ", "translated"))); var diagnostics = LanguageFileValidation.ValidateTarget( source, target, new LanguageFileValidationOptions(requireCompleteBundle: true, minimumTargetState: "translated")); var diagnostic = diagnostics.ShouldHaveSingleItem(); diagnostic.Kind.ShouldBe(LanguageFileValidationDiagnosticKind.InvalidTranslation); diagnostic.Message.ShouldContain("must contain a target"); } [Fact] public void ValidateTarget_Reports_Actual_And_Required_Target_State() { var source = Parse(SourceXliff(("Title", "Hello"))); var target = Parse(TargetXliff( "ja-JP", ("Title", "Hello", "こんにちは", "reviewed"))); var diagnostics = LanguageFileValidation.ValidateTarget( source, target, new LanguageFileValidationOptions(requireCompleteBundle: true, minimumTargetState: "final")); var diagnostic = diagnostics.ShouldHaveSingleItem(); diagnostic.Kind.ShouldBe(LanguageFileValidationDiagnosticKind.InvalidTranslation); diagnostic.Message.ShouldContain("reviewed"); diagnostic.Message.ShouldContain("final"); } [Fact] public void ValidateTargetContent_Validates_Deferred_Target_Without_Source_Contract() { var target = Parse(TargetXliff( "ja-JP", ("Title", "Hello", "こんにちは", "reviewed"))); var diagnostics = LanguageFileValidation.ValidateTargetContent( target, new LanguageFileValidationOptions(requireCompleteBundle: true, minimumTargetState: "final")); var diagnostic = diagnostics.ShouldHaveSingleItem(); diagnostic.Kind.ShouldBe(LanguageFileValidationDiagnosticKind.InvalidTranslation); diagnostic.Message.ShouldContain("reviewed"); diagnostic.Message.ShouldContain("final"); } private static XliffDocumentModel Parse(string content) { var result = Xliff21Parser.Parse(content); result.Errors.ShouldBeEmpty(); return result.Document.ShouldNotBeNull(); } private static string SourceXliff(params (string Key, string Source)[] units) { return $$""" {{string.Join(Environment.NewLine, units.Select(static unit => $$""" {{unit.Source}} """))}} """; } private static string TargetXliff( string language, params (string Key, string Source, string Target, string State)[] units) { return $$""" {{string.Join(Environment.NewLine, units.Select(static unit => $$""" {{unit.Source}}{{unit.Target}} """))}} """; } }