ui: move translation credentials into settings
This commit is contained in:
@@ -56,6 +56,8 @@ private func waitForTranslation(_ condition: () -> Bool) async throws {
|
||||
@MainActor
|
||||
private func descendants(_ view: NSView) -> [NSView] { [view] + view.subviews.flatMap(descendants) }
|
||||
|
||||
@Suite(.serialized)
|
||||
struct TranslationExecutionTests {
|
||||
@Test @MainActor
|
||||
func translationExecutionIsExplicitAndMyMemoryNeverTouchesCredentials() async throws {
|
||||
_ = NSApplication.shared
|
||||
@@ -100,7 +102,7 @@ func translationExecutionRejectsOldCompletionsWithoutClearingNewRequests() async
|
||||
tool.input = { input }
|
||||
let views = descendants(tool.view)
|
||||
let output = try #require(views.compactMap { $0 as? NSTextView }.first)
|
||||
let provider = try #require(views.compactMap { $0 as? NSPopUpButton }.first)
|
||||
let provider = tool.providerPicker
|
||||
let button = try #require(views.compactMap { $0 as? NSButton }.first { $0.action == NSSelectorFromString("translate") })
|
||||
tool.translate()
|
||||
try await waitForTranslation { requests.pending.count == 1 }
|
||||
@@ -153,29 +155,39 @@ func translationExecutionRecoversFromCredentialAndRequestFailures() async throws
|
||||
openAI: { try await requests.translate($0, key: $1) })
|
||||
tool.input = { "Hello" }
|
||||
let views = descendants(tool.view)
|
||||
let field = try #require(views.compactMap { $0 as? NSSecureTextField }.first)
|
||||
let settings = TranslationSettingsController(credentials: keys.store)
|
||||
let settingsViews = descendants(settings.view)
|
||||
let field = try #require(settingsViews.compactMap { $0 as? NSSecureTextField }.first)
|
||||
let output = try #require(views.compactMap { $0 as? NSTextView }.first)
|
||||
let status = try #require(views.compactMap { $0 as? NSTextField }.first { $0.accessibilityLabel() == "Translation status and errors" })
|
||||
let button = try #require(views.compactMap { $0 as? NSButton }.first { $0.action == NSSelectorFromString("translate") })
|
||||
let forget = try #require(views.compactMap { $0 as? NSButton }.first { $0.action == NSSelectorFromString("forgetKey") })
|
||||
let save = try #require(settingsViews.compactMap { $0 as? NSButton }.first { $0.action == NSSelectorFromString("saveKey") })
|
||||
let forget = try #require(settingsViews.compactMap { $0 as? NSButton }.first { $0.action == NSSelectorFromString("forgetKey") })
|
||||
let openSettings = try #require(views.compactMap { $0 as? NSButton }.first { $0.action == NSSelectorFromString("openSettings") })
|
||||
|
||||
tool.translate() // No stored key.
|
||||
#expect(requests.inputs.isEmpty)
|
||||
#expect(button.isEnabled)
|
||||
#expect(status.textColor == .systemRed)
|
||||
#expect(!openSettings.isHidden)
|
||||
field.stringValue = "typed-key"
|
||||
tool.translate() // Typing in Settings must not implicitly save on Translate.
|
||||
#expect(requests.inputs.isEmpty)
|
||||
#expect(keys.value == nil)
|
||||
#expect(field.stringValue == "typed-key")
|
||||
keys.denied = true
|
||||
tool.translate() // A failed save must not silently send the typed key.
|
||||
save.performClick(nil) // A failed save must retain the typed key for retry.
|
||||
#expect(requests.inputs.isEmpty)
|
||||
#expect(field.stringValue == "typed-key")
|
||||
#expect(keys.value == nil)
|
||||
#expect(status.stringValue.contains("Credential access denied"))
|
||||
field.stringValue = ""
|
||||
#expect(settingsViews.compactMap { $0 as? NSTextField }.contains { $0.stringValue.contains("Credential access denied") })
|
||||
tool.translate() // Failed reads are also recoverable without a request.
|
||||
#expect(requests.inputs.isEmpty)
|
||||
#expect(button.isEnabled)
|
||||
keys.denied = false
|
||||
field.stringValue = "typed-key"
|
||||
save.performClick(nil)
|
||||
#expect(keys.value == "typed-key")
|
||||
#expect(field.stringValue.isEmpty)
|
||||
tool.translate()
|
||||
try await waitForTranslation { requests.pending.count == 1 }
|
||||
#expect(keys.value == "typed-key")
|
||||
@@ -184,6 +196,11 @@ func translationExecutionRecoversFromCredentialAndRequestFailures() async throws
|
||||
requests.pending[0].resume(throwing: CredentialFailure())
|
||||
try await waitForTranslation { button.isEnabled }
|
||||
#expect(status.textColor == .systemRed)
|
||||
keys.denied = true
|
||||
forget.performClick(nil)
|
||||
#expect(keys.value == "typed-key")
|
||||
#expect(settingsViews.compactMap { $0 as? NSTextField }.contains { $0.stringValue.contains("Credential access denied") })
|
||||
keys.denied = false
|
||||
tool.translate()
|
||||
try await waitForTranslation { requests.pending.count == 2 }
|
||||
forget.performClick(nil)
|
||||
@@ -199,3 +216,4 @@ func translationExecutionRecoversFromCredentialAndRequestFailures() async throws
|
||||
try await waitForTranslation { output.string == "成功" }
|
||||
#expect(status.textColor != .systemRed)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user