mirror of
https://github.com/phranck/TUIkit.git
synced 2026-05-21 09:50:35 +00:00
db8ea40c0a
- Fix 80 SwiftLint warnings (159 -> 79): vertical_whitespace, prefer_self_in_static_references, modifier_order, trailing_newline, trailing_whitespace, prefer_for_where, unneeded_synthesized_initializer, redundant_type_annotation, implicit_optional_initialization, superfluous_disable_command, shorthand_optional_binding, syntactic_sugar, empty_string, vertical_whitespace_closing_braces, identifier_name in BadgeModifier - Refactor StatusBar from direct Renderable to _StatusBarCore pattern (public View with real body wrapping private Renderable core)
358 lines
13 KiB
Swift
358 lines
13 KiB
Swift
// 🖥️ TUIKit — Terminal UI Kit for Swift
|
||
// LocalizationServiceTests.swift
|
||
//
|
||
// Created by LAYERED.work
|
||
// License: MIT
|
||
|
||
import Foundation
|
||
import Testing
|
||
@testable import TUIkit
|
||
|
||
// MARK: - Localization Service Tests
|
||
|
||
@Suite("LocalizationService")
|
||
final class LocalizationServiceTests {
|
||
var sut: LocalizationService
|
||
|
||
init() {
|
||
sut = LocalizationService()
|
||
sut.setLanguage(.english)
|
||
}
|
||
|
||
// MARK: - Bundle Loading Tests
|
||
|
||
@Test("Loads English translations from bundle")
|
||
func loadEnglishTranslations() {
|
||
let sut = LocalizationService()
|
||
let englishStrings = sut.string(for: "button.ok")
|
||
#expect(englishStrings == "OK")
|
||
}
|
||
|
||
@Test("Loads German translations from bundle")
|
||
func loadGermanTranslations() {
|
||
let sut = LocalizationService()
|
||
sut.setLanguage(.german)
|
||
let germanStrings = sut.string(for: "button.ok")
|
||
#expect(germanStrings == "OK")
|
||
}
|
||
|
||
@Test("Loads French translations from bundle")
|
||
func loadFrenchTranslations() {
|
||
let sut = LocalizationService()
|
||
sut.setLanguage(.french)
|
||
let frenchStrings = sut.string(for: "button.cancel")
|
||
#expect(frenchStrings == "Annuler")
|
||
}
|
||
|
||
@Test("Loads Italian translations from bundle")
|
||
func loadItalianTranslations() {
|
||
let sut = LocalizationService()
|
||
sut.setLanguage(.italian)
|
||
let italianStrings = sut.string(for: "button.yes")
|
||
#expect(italianStrings == "Sì")
|
||
}
|
||
|
||
@Test("Loads Spanish translations from bundle")
|
||
func loadSpanishTranslations() {
|
||
let sut = LocalizationService()
|
||
sut.setLanguage(.spanish)
|
||
let spanishStrings = sut.string(for: "button.no")
|
||
#expect(spanishStrings == "No")
|
||
}
|
||
|
||
// MARK: - String Resolution Tests
|
||
|
||
@Test("Resolves dot-notation keys")
|
||
func resolvesDotNotationKeys() {
|
||
let string = sut.string(for: "button.ok")
|
||
#expect(string == "OK")
|
||
}
|
||
|
||
@Test("Resolves nested keys")
|
||
func resolvesNestedKeys() {
|
||
let string = sut.string(for: "error.invalid_input")
|
||
#expect(string == "Invalid input")
|
||
}
|
||
|
||
@Test("Resolves all key categories")
|
||
func resolvesAllKeyCategories() {
|
||
#expect(sut.string(for: "button.save") == "Save")
|
||
#expect(sut.string(for: "label.name") == "Name")
|
||
#expect(sut.string(for: "error.not_found") == "Not found")
|
||
#expect(sut.string(for: "placeholder.search") == "Search...")
|
||
#expect(sut.string(for: "menu.file") == "File")
|
||
#expect(sut.string(for: "dialog.confirm") == "Confirm")
|
||
#expect(sut.string(for: "validation.email_invalid") == "Invalid email address")
|
||
}
|
||
|
||
// MARK: - Fallback Tests
|
||
|
||
@Test("Falls back to English when key missing in current language")
|
||
func fallsBackToEnglish() {
|
||
sut.setLanguage(.german)
|
||
let string = sut.string(for: "button.ok")
|
||
#expect(string != "button.ok")
|
||
}
|
||
|
||
@Test("Returns key when not found in any language")
|
||
func returnsKeyWhenNotFound() {
|
||
let unknownKey = "nonexistent.key.that.does.not.exist"
|
||
let string = sut.string(for: unknownKey)
|
||
#expect(string == unknownKey)
|
||
}
|
||
|
||
@Test("Fallback chain works correctly")
|
||
func fallbackChain() {
|
||
sut.setLanguage(.german)
|
||
let germanString = sut.string(for: "button.ok")
|
||
#expect(germanString == "OK")
|
||
|
||
sut.setLanguage(.english)
|
||
let englishString = sut.string(for: "button.ok")
|
||
#expect(englishString == "OK")
|
||
#expect(germanString == englishString)
|
||
}
|
||
|
||
// MARK: - Language Switching Tests
|
||
|
||
@Test("Switches language successfully")
|
||
func switchesLanguage() {
|
||
sut.setLanguage(.english)
|
||
#expect(sut.currentLanguage == .english)
|
||
|
||
sut.setLanguage(.german)
|
||
#expect(sut.currentLanguage == .german)
|
||
|
||
sut.setLanguage(.french)
|
||
#expect(sut.currentLanguage == .french)
|
||
}
|
||
|
||
@Test("Language property returns current language")
|
||
func languageProperty() {
|
||
sut.setLanguage(.english)
|
||
#expect(sut.currentLanguage == .english)
|
||
|
||
sut.setLanguage(.italian)
|
||
#expect(sut.currentLanguage == .italian)
|
||
}
|
||
|
||
@Test("Resolves strings after language switch")
|
||
func resolvesStringsAfterSwitch() {
|
||
sut.setLanguage(.english)
|
||
var string = sut.string(for: "button.save")
|
||
#expect(string == "Save")
|
||
|
||
sut.setLanguage(.german)
|
||
string = sut.string(for: "button.save")
|
||
#expect(string == "Speichern")
|
||
|
||
sut.setLanguage(.french)
|
||
string = sut.string(for: "button.save")
|
||
#expect(string == "Enregistrer")
|
||
}
|
||
|
||
// MARK: - Persistence Tests
|
||
|
||
@Test("Saves language preference to config file")
|
||
func savesLanguagePreference() throws {
|
||
let tempDir = NSTemporaryDirectory() + "tuikit-test-\(UUID().uuidString)"
|
||
defer { try? FileManager.default.removeItem(atPath: tempDir) }
|
||
|
||
let service = LocalizationService(configDirectoryPath: tempDir)
|
||
service.setLanguage(.german)
|
||
|
||
let path = (tempDir as NSString).appendingPathComponent("language")
|
||
let content = try String(contentsOfFile: path, encoding: .utf8).trimmingCharacters(
|
||
in: .whitespacesAndNewlines
|
||
)
|
||
#expect(content == "de")
|
||
}
|
||
|
||
@Test("Loads language preference from config file")
|
||
func loadsLanguagePreference() throws {
|
||
let tempDir = NSTemporaryDirectory() + "tuikit-test-\(UUID().uuidString)"
|
||
defer { try? FileManager.default.removeItem(atPath: tempDir) }
|
||
|
||
try FileManager.default.createDirectory(
|
||
atPath: tempDir,
|
||
withIntermediateDirectories: true,
|
||
attributes: nil
|
||
)
|
||
let path = (tempDir as NSString).appendingPathComponent("language")
|
||
try "fr".write(toFile: path, atomically: true, encoding: .utf8)
|
||
|
||
let service = LocalizationService(configDirectoryPath: tempDir)
|
||
#expect(service.currentLanguage == .french)
|
||
}
|
||
|
||
@Test("Handles missing config file gracefully")
|
||
func handlesMissingConfigFile() {
|
||
let tempDir = NSTemporaryDirectory() + "tuikit-test-\(UUID().uuidString)"
|
||
defer { try? FileManager.default.removeItem(atPath: tempDir) }
|
||
|
||
let service = LocalizationService(configDirectoryPath: tempDir)
|
||
#expect(service.currentLanguage == .english)
|
||
}
|
||
|
||
@Test("Handles invalid config file content gracefully")
|
||
func handlesInvalidConfigFile() throws {
|
||
let tempDir = NSTemporaryDirectory() + "tuikit-test-\(UUID().uuidString)"
|
||
defer { try? FileManager.default.removeItem(atPath: tempDir) }
|
||
|
||
try FileManager.default.createDirectory(
|
||
atPath: tempDir,
|
||
withIntermediateDirectories: true,
|
||
attributes: nil
|
||
)
|
||
let path = (tempDir as NSString).appendingPathComponent("language")
|
||
try "invalid_lang_code".write(toFile: path, atomically: true, encoding: .utf8)
|
||
|
||
let service = LocalizationService(configDirectoryPath: tempDir)
|
||
#expect(service.currentLanguage == .english)
|
||
}
|
||
|
||
// MARK: - Language Enum Tests
|
||
|
||
@Test("Language enum raw values are correct")
|
||
func languageRawValues() {
|
||
#expect(LocalizationService.Language.english.rawValue == "en")
|
||
#expect(LocalizationService.Language.german.rawValue == "de")
|
||
#expect(LocalizationService.Language.french.rawValue == "fr")
|
||
#expect(LocalizationService.Language.italian.rawValue == "it")
|
||
#expect(LocalizationService.Language.spanish.rawValue == "es")
|
||
}
|
||
|
||
@Test("Language enum display names are correct")
|
||
func languageDisplayNames() {
|
||
#expect(LocalizationService.Language.english.displayName == "English")
|
||
#expect(LocalizationService.Language.german.displayName == "Deutsch")
|
||
#expect(LocalizationService.Language.french.displayName == "Français")
|
||
#expect(LocalizationService.Language.italian.displayName == "Italiano")
|
||
#expect(LocalizationService.Language.spanish.displayName == "Español")
|
||
}
|
||
|
||
@Test("Language enum is codable")
|
||
func languageIsCodable() {
|
||
let encoder = JSONEncoder()
|
||
let decoder = JSONDecoder()
|
||
|
||
let english = LocalizationService.Language.english
|
||
let encodedEnglish = try? encoder.encode(english)
|
||
let decodedEnglish = try? decoder.decode(LocalizationService.Language.self, from: encodedEnglish ?? Data())
|
||
#expect(decodedEnglish == english)
|
||
|
||
let german = LocalizationService.Language.german
|
||
let encodedGerman = try? encoder.encode(german)
|
||
let decodedGerman = try? decoder.decode(LocalizationService.Language.self, from: encodedGerman ?? Data())
|
||
#expect(decodedGerman == german)
|
||
}
|
||
|
||
// MARK: - Caching Tests
|
||
|
||
@Test("Caches translation dictionaries")
|
||
func cacheTranslations() {
|
||
_ = sut.string(for: "button.ok")
|
||
|
||
let string1 = sut.string(for: "button.ok")
|
||
let string2 = sut.string(for: "button.ok")
|
||
#expect(string1 == string2)
|
||
}
|
||
|
||
@Test("Cache is per-language")
|
||
func cacheIsPerLanguage() {
|
||
sut.setLanguage(.english)
|
||
let englishString = sut.string(for: "button.save")
|
||
#expect(englishString == "Save")
|
||
|
||
sut.setLanguage(.german)
|
||
let germanString = sut.string(for: "button.save")
|
||
#expect(germanString == "Speichern")
|
||
|
||
sut.setLanguage(.english)
|
||
let englishAgain = sut.string(for: "button.save")
|
||
#expect(englishAgain == "Save")
|
||
}
|
||
}
|
||
|
||
// MARK: - Localization Key Tests
|
||
|
||
@Suite("LocalizationKey")
|
||
final class LocalizationKeyTests {
|
||
var service: LocalizationService
|
||
|
||
init() {
|
||
service = LocalizationService()
|
||
service.setLanguage(.english)
|
||
}
|
||
|
||
@Test("Button keys resolve correctly")
|
||
func buttonKeys() {
|
||
#expect(service.string(for: LocalizationKey.Button.ok) == "OK")
|
||
#expect(service.string(for: LocalizationKey.Button.cancel) == "Cancel")
|
||
#expect(service.string(for: LocalizationKey.Button.save) == "Save")
|
||
#expect(service.string(for: LocalizationKey.Button.delete) == "Delete")
|
||
}
|
||
|
||
@Test("Label keys resolve correctly")
|
||
func labelKeys() {
|
||
#expect(service.string(for: LocalizationKey.Label.name) == "Name")
|
||
#expect(service.string(for: LocalizationKey.Label.description) == "Description")
|
||
#expect(service.string(for: LocalizationKey.Label.value) == "Value")
|
||
#expect(service.string(for: LocalizationKey.Label.status) == "Status")
|
||
}
|
||
|
||
@Test("Error keys resolve correctly")
|
||
func errorKeys() {
|
||
#expect(service.string(for: LocalizationKey.Error.invalidInput) == "Invalid input")
|
||
#expect(service.string(for: LocalizationKey.Error.notFound) == "Not found")
|
||
#expect(service.string(for: LocalizationKey.Error.accessDenied) == "Access denied")
|
||
}
|
||
|
||
@Test("Placeholder keys resolve correctly")
|
||
func placeholderKeys() {
|
||
#expect(service.string(for: LocalizationKey.Placeholder.search) == "Search...")
|
||
#expect(service.string(for: LocalizationKey.Placeholder.enterText) == "Enter text...")
|
||
#expect(service.string(for: LocalizationKey.Placeholder.selectOption) == "Select an option...")
|
||
}
|
||
|
||
@Test("Menu keys resolve correctly")
|
||
func menuKeys() {
|
||
#expect(service.string(for: LocalizationKey.Menu.file) == "File")
|
||
#expect(service.string(for: LocalizationKey.Menu.edit) == "Edit")
|
||
#expect(service.string(for: LocalizationKey.Menu.view) == "View")
|
||
}
|
||
|
||
@Test("Dialog keys resolve correctly")
|
||
func dialogKeys() {
|
||
#expect(service.string(for: LocalizationKey.Dialog.confirm) == "Confirm")
|
||
#expect(service.string(for: LocalizationKey.Dialog.deleteConfirmation) == "Are you sure you want to delete this?")
|
||
#expect(service.string(for: LocalizationKey.Dialog.success) == "Operation completed successfully")
|
||
}
|
||
|
||
@Test("Validation keys resolve correctly")
|
||
func validationKeys() {
|
||
#expect(service.string(for: LocalizationKey.Validation.emailInvalid) == "Invalid email address")
|
||
#expect(service.string(for: LocalizationKey.Validation.passwordTooShort) == "Password must be at least 8 characters")
|
||
#expect(service.string(for: LocalizationKey.Validation.usernameTaken) == "Username already exists")
|
||
}
|
||
|
||
@Test("Keys work across all languages")
|
||
func keysAcrossLanguages() {
|
||
service.setLanguage(.german)
|
||
#expect(service.string(for: LocalizationKey.Button.ok) == "OK")
|
||
|
||
service.setLanguage(.french)
|
||
#expect(service.string(for: LocalizationKey.Button.cancel) == "Annuler")
|
||
|
||
service.setLanguage(.italian)
|
||
#expect(service.string(for: LocalizationKey.Error.notFound) == "Non trovato")
|
||
}
|
||
|
||
@Test("Key enum raw values match string keys")
|
||
func keyRawValues() {
|
||
#expect(LocalizationKey.Button.ok.rawValue == "button.ok")
|
||
#expect(LocalizationKey.Error.notFound.rawValue == "error.not_found")
|
||
#expect(LocalizationKey.Label.name.rawValue == "label.name")
|
||
}
|
||
}
|