native macOS codings agent orchestrator prowl.onev.cat
Something went wrong. Try again.
8.3 kB · 230 lines
Swift
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231import Foundationimport Testing
@testable import supacode
struct AppLanguageTests { private let supportedLanguages = ["en", "zh-Hans"]
// MARK: - Model
@Test func rawValuesAreStableStorageIdentifiers() { #expect(AppLanguage.system.rawValue == "system") #expect(AppLanguage.zhHans.rawValue == "zh-Hans") #expect(AppLanguage.english.rawValue == "en") #expect(AppLanguage(rawValue: "future-language") == nil) }
@Test func titlesUseNativeLanguageForms() throws { #expect(AppLanguage.system.title == "Follow System") #expect(try chinese("Follow System") == "跟随系统") #expect(AppLanguage.zhHans.title == "简体中文") #expect(AppLanguage.english.title == "English") }
@Test func chinesePullRequestSummaryPreservesBaseAndHeadRoles() throws { let template = try chinese("%@ wants to merge %@ %@ into %@ from %@") let summary = String(format: template, "alice", "2", "commits", "main", "feature")
#expect(summary == "alice 想要将 2 commits 从 feature 合并到 main") }
@Test func chineseSkillRemovalHelpPreservesSkillAndPathRoles() throws { let template = try chinese("Remove the %@ skill link at %@; the bundled skill stays in the app") let help = String(format: template, "reviewer", "/tmp/reviewer")
#expect(help == "移除 /tmp/reviewer 处的 reviewer 技能链接;内置技能仍保留在应用中") }
@Test func confirmedWorkflowUIStringsHaveChineseTranslations() throws { let expectedTranslations = [ "Delete Run": "删除运行记录", "Workflow run options": "工作流运行选项", "No fields": "无字段", ]
for (key, expected) in expectedTranslations { #expect(try chinese(key) == expected) } }
@Test func resolvedLanguageIsOnlyEnOrZhHans() { #expect(Set(ResolvedAppLanguage.allCases.map(\.rawValue)) == ["en", "zh-Hans"]) }
@Test func effectiveLanguageIsASupportedLanguage() { #expect(ResolvedAppLanguage.allCases.contains(ResolvedAppLanguage.effective())) }
// MARK: - Resolution
@Test func explicitPreferenceWinsOverPlatformLanguages() { #expect( AppLanguageResolver.resolve( preference: .english, platformLanguages: ["zh-Hans"], supportedLanguages: supportedLanguages ) == .english ) #expect( AppLanguageResolver.resolve( preference: .zhHans, platformLanguages: ["en-US"], supportedLanguages: supportedLanguages ) == .zhHans ) }
@Test func systemPreferenceFollowsPlatformNegotiation() { #expect( AppLanguageResolver.resolve( preference: .system, platformLanguages: ["zh-Hans", "en"], supportedLanguages: supportedLanguages ) == .zhHans ) #expect( AppLanguageResolver.resolve( preference: .system, platformLanguages: ["en-US", "zh-Hans"], supportedLanguages: supportedLanguages ) == .english ) }
@Test func systemPreferenceFallsBackToEnglishWhenNothingMatches() { #expect( AppLanguageResolver.resolve( preference: .system, platformLanguages: ["fr-FR"], supportedLanguages: supportedLanguages ) == .english ) #expect( AppLanguageResolver.resolve( preference: .system, platformLanguages: [], supportedLanguages: supportedLanguages ) == .english ) }
@Test func traditionalChineseDoesNotResolveToSimplified() { // Script differs, so platform matching must fall back to English rather // than treating any "zh" prefix as Simplified. #expect( AppLanguageResolver.resolve( preference: .system, platformLanguages: ["zh-Hant-TW"], supportedLanguages: supportedLanguages ) == .english ) }
// MARK: - Store
// Prowl keeps no copy of the choice. The per-app `AppleLanguages` default is the only // source, and macOS writes the same key from System Settings. @Test func absentKeyMeansFollowSystem() { let (defaults, suite) = makeIsolatedDefaults() defer { UserDefaults().removePersistentDomain(forName: suite) } let store = AppLanguageStore(defaults: defaults, domainName: suite)
#expect(store.appleLanguages == nil) #expect(store.language == .system) }
@Test func explicitChoiceWritesOneLanguageAndSystemRemovesTheKey() { let (defaults, suite) = makeIsolatedDefaults() defer { UserDefaults().removePersistentDomain(forName: suite) } let store = AppLanguageStore(defaults: defaults, domainName: suite)
store.setLanguage(.zhHans) #expect(store.appleLanguages == ["zh-Hans"]) #expect(store.language == .zhHans)
store.setLanguage(.english) #expect(store.appleLanguages == ["en"]) #expect(store.language == .english)
store.setLanguage(.system) #expect(store.appleLanguages == nil) #expect(store.language == .system) }
@Test func choiceMadeInSystemSettingsIsReadBack() { let (defaults, suite) = makeIsolatedDefaults() defer { UserDefaults().removePersistentDomain(forName: suite) } let store = AppLanguageStore(defaults: defaults, domainName: suite)
// System Settings writes regional identifiers and a fallback list. defaults.set(["zh-Hans-CN", "en-CN"], forKey: AppLanguageStore.appleLanguagesKey) #expect(store.language == .zhHans)
defaults.set(["en-GB"], forKey: AppLanguageStore.appleLanguagesKey) #expect(store.language == .english) }
@Test func languageWithoutLocalizationReadsAsEnglish() { let (defaults, suite) = makeIsolatedDefaults() defer { UserDefaults().removePersistentDomain(forName: suite) } let store = AppLanguageStore(defaults: defaults, domainName: suite)
// The picker shows the language the app displays, and that is the English fallback. defaults.set(["ja-JP"], forKey: AppLanguageStore.appleLanguagesKey) #expect(store.language == .english)
defaults.set(["zh-Hant-TW"], forKey: AppLanguageStore.appleLanguagesKey) #expect(store.language == .english) }
@Test func followSystemRemovesAValueThatSystemSettingsWrote() { let (defaults, suite) = makeIsolatedDefaults() defer { UserDefaults().removePersistentDomain(forName: suite) } let store = AppLanguageStore(defaults: defaults, domainName: suite) defaults.set(["zh-Hans-CN"], forKey: AppLanguageStore.appleLanguagesKey)
store.setLanguage(.system) #expect(store.appleLanguages == nil) }
// A shortcut title is a run-time key: the compiler cannot extract it, so // `make audit-localization` does not see a missing entry. @Test func everyShortcutTitleHasSimplifiedChineseEntry() throws { let path = try #require(Bundle.main.path(forResource: "zh-Hans", ofType: "lproj")) let bundle = try #require(Bundle(path: path)) let sentinel = "\u{1}" let missing = AppShortcuts.bindings.map(\.title).filter { bundle.localizedString(forKey: $0, value: sentinel, table: nil) == sentinel } #expect(missing.isEmpty, "Add zh-Hans entries with extraction state manual: \(missing)") }
// "Done" and "Blocked" have other meanings elsewhere (a button, a pull request // that cannot merge), so the agent states use their own keys. @Test func agentStateLabelsHaveTheirOwnCatalogEntries() throws { #expect(AgentDisplayState.working.label == "Working") #expect(AgentDisplayState.blocked.label == "Blocked") #expect(AgentDisplayState.done.label == "Done") #expect(AgentDisplayState.idle.label == "Idle") #expect(try chinese("agentState.working") == "工作中") #expect(try chinese("agentState.blocked") == "需处理") #expect(try chinese("agentState.done") == "已完成") #expect(try chinese("agentState.idle") == "空闲") }
private func chinese(_ key: String) throws -> String { let path = try #require(Bundle.main.path(forResource: "zh-Hans", ofType: "lproj")) let bundle = try #require(Bundle(path: path)) return bundle.localizedString(forKey: key, value: nil, table: nil) }
private func makeIsolatedDefaults( function: String = #function ) -> (UserDefaults, String) { let suite = "prowl-app-language-tests.\(function).\(UUID().uuidString)" let defaults = UserDefaults(suiteName: suite)! defaults.removePersistentDomain(forName: suite) return (defaults, suite) }}