feat: refine native iOS chat experience

This commit is contained in:
snowspeeder committed 2026-10-03 18:55:28 -04:00
1 parent 9c5e9bce5b
commit 7bd893ae5c
14 files changed
+415 -93

No files matched your search

+24
View File
@@ -28,6 +28,30 @@ struct AuthTests {
config.protocolClasses = [AuthURLProtocol.self]
return URLSession(configuration: config)
}
@Test func englishVoiceCatalogAndPreferenceMigration() async throws {
AuthURLProtocol.handler = { request in
#expect(request.url?.path == "/voices")
#expect(request.httpMethod == "GET")
#expect(request.value(forHTTPHeaderField: "Authorization") == "Bearer test-session")
return (200, Data(#"{"default_voice":"en-US-Ava:DragonHDOmniLatestNeural","catalog_available":true,"voices":[{"id":"en-GB-Sonia:DragonHDOmniLatestNeural","name":"Sonia","locale":"en-GB","gender":"Female","tag":"Omni"}]}"#.utf8))
}
let catalog = try await AuthClient(config: config(), session: session()).voices(token: "test-session")
#expect(catalog.catalogAvailable)
#expect(catalog.voices.first?.details == "en-GB · Female · Omni")
#expect(Voices.restoredID(catalog.voices.first?.id) == "en-GB-Sonia:DragonHDOmniLatestNeural")
// A valid server-catalogued choice must survive SettingsStore's next launch,
// even if it is not among the offline fallback voices.
#expect(Voices.restoredID("en-GB-Sonia:DragonHDOmniLatestNeural") == "en-GB-Sonia:DragonHDOmniLatestNeural")
#expect(Voices.restoredID("en-US-Ava:DragonHDLatestNeural") == Voices.defaultID)
for legacy in ["Jenny", "Guy", "Andrew", "Aria", "Emma", "Eric", "Brian", "Ashley", "Richard", "Tina", "Steffan"] {
let migrated = Voices.restoredID("en-US-\(legacy)Neural")
#expect(migrated.contains(":DragonHDOmniLatestNeural"))
#expect(Voices.display(for: migrated) != nil)
}
#expect(Voices.restoredID("invalid <voice>") == Voices.defaultID)
#expect(Voices.restoredID(nil) == Voices.defaultID)
}
@Test func certificateFailureHasSafeRecovery() throws {
let url = URL(string: "https://192.168.86.2:9178")!
let message = ConnectionFailure.message(URLError(.serverCertificateUntrusted), server: url)
+25
View File
@@ -35,6 +35,31 @@ final class ChatTests: XCTestCase {
XCTAssertNil(store.selected)
XCTAssertTrue(store.messages.isEmpty)
}
func testWorkspaceSaveReportsFailureAndPreservesExistingWorkspaces() async throws {
let store = ChatStore(api: OfflineChatAPI())
let rejected = await store.saveWorkspace(nil, name: " ", instructions: "Ignored")
XCTAssertFalse(rejected)
XCTAssertEqual(store.error, "Enter a workspace name.")
XCTAssertTrue(store.workspaces.isEmpty)
let saved = await store.saveWorkspace(nil, name: "Writing", instructions: "Be concise")
XCTAssertTrue(saved)
XCTAssertEqual(store.workspaces.map(\.name), ["Writing"])
}
func testPreviewClearsOldContentBeforeNewRequestAndOnTeardown() async throws {
let api = OfflineChatAPI()
let store = ChatStore(api: api)
await store.features.load()
let data = Data("Private preview".utf8)
_ = await store.features.upload(data, filename: "preview.txt", mime: "text/plain")
let attachment = try XCTUnwrap(store.features.attachments.first)
store.features.previewText = "stale"
await store.features.preview(attachment)
XCTAssertEqual(store.features.previewAttachment?.id, attachment.id)
XCTAssertNotEqual(store.features.previewText, "stale")
store.features.clearPreview()
XCTAssertNil(store.features.previewAttachment)
XCTAssertNil(store.features.previewText)
}
func testOfflineResearchTruthfulExportAndTeardown() async throws {
let store = ChatStore(api: OfflineChatAPI())
await store.create()