mirror of
https://github.com/sudosylabs/vnidrop.git
synced 2026-08-05 02:29:55 +02:00
Replace the free-form InvitationError.message(String) case with semantic
cases mapped to L10n keys at the UI boundary (Error.uiText), so user-facing
error text is localized instead of substring-matched from English blobs.
.raw(String) remains only for genuinely dynamic system/core messages.
Localize the CoreNFC alertMessage prompts via existing L10n keys, and add
SwiftLint rules (raw_alert_message, raw_invitation_error) to catch raw
alert strings and literal .raw("…") errors going forward.
69 lines
2.9 KiB
Swift
69 lines
2.9 KiB
Swift
import XCTest
|
|
import VnidropCore
|
|
@testable import VniDrop
|
|
|
|
/// Ports `ui/feedback/UiMessageControllerTest.kt` and `UserFacingErrorTest.kt`.
|
|
@MainActor
|
|
final class UiMessageControllerTests: XCTestCase {
|
|
|
|
func testQueuesAndAdvances() {
|
|
let c = UiMessageController()
|
|
c.show(UiMessage(text: .dynamic("first")))
|
|
c.show(UiMessage(text: .dynamic("second")))
|
|
XCTAssertEqual(c.current?.text, .dynamic("first"))
|
|
|
|
c.advance()
|
|
XCTAssertEqual(c.current?.text, .dynamic("second"))
|
|
|
|
c.advance()
|
|
XCTAssertNil(c.current)
|
|
}
|
|
|
|
func testErrorSuppressesUserCancellation() {
|
|
let c = UiMessageController()
|
|
c.error(InvitationError.cancelled)
|
|
XCTAssertNil(c.current) // cancellations are swallowed
|
|
}
|
|
|
|
func testErrorShowsNonCancellation() {
|
|
let c = UiMessageController()
|
|
c.error(InvitationError.raw("The transfer was refused"))
|
|
XCTAssertEqual(c.current?.tone, .error)
|
|
}
|
|
}
|
|
|
|
@MainActor
|
|
final class UserFacingErrorTests: XCTestCase {
|
|
|
|
func testIsUserCancellation() {
|
|
XCTAssertTrue(InvitationError.cancelled.isUserCancellation)
|
|
XCTAssertTrue(InvitationError.raw("User canceled the picker").isUserCancellation)
|
|
XCTAssertFalse(InvitationError.raw("A database error occurred").isUserCancellation)
|
|
}
|
|
|
|
func testToUiTextMapsKnownReasons() {
|
|
// Typed cases map directly at the UI boundary.
|
|
XCTAssertEqual(InvitationError.shareEmpty.toUiText(), .resource(L10n.Error.shareEmpty))
|
|
XCTAssertEqual(InvitationError.cameraUnavailable.toUiText(), .resource(L10n.Error.camera))
|
|
XCTAssertEqual(InvitationError.nfcFailed.toUiText(), .resource(L10n.Error.nfc))
|
|
// Dynamic `.raw` payloads still fall through the substring hints.
|
|
XCTAssertEqual(InvitationError.raw("The transfer was refused").toUiText(), .resource(L10n.Error.permission))
|
|
XCTAssertEqual(InvitationError.raw("invalid ticket").toUiText(), .resource(L10n.Error.invalidTicket))
|
|
}
|
|
|
|
func testToUiTextFallsBackToGeneric() {
|
|
XCTAssertEqual(InvitationError.raw("something entirely unexpected").toUiText(), .resource(L10n.Error.generic))
|
|
}
|
|
|
|
func testToUiTextMapsTypedTransferFailures() {
|
|
XCTAssertEqual(VnidropError.FilesystemPermission(reason: "read-only folder").toUiText(), .resource(L10n.Error.filesystem))
|
|
XCTAssertEqual(VnidropError.DestinationExists(reason: "target exists").toUiText(), .resource(L10n.Error.destinationExists))
|
|
XCTAssertEqual(VnidropError.StorageFull(reason: "disk full").toUiText(), .resource(L10n.Error.storageFull))
|
|
XCTAssertEqual(VnidropError.Network(reason: "offline").toUiText(), .resource(L10n.Error.network))
|
|
XCTAssertEqual(VnidropError.InvalidInput(reason: "bad path").toUiText(), .resource(L10n.Error.invalidInput))
|
|
XCTAssertFalse(VnidropError.FilesystemPermission(reason: "read-only").canRetryWithoutChangingInput)
|
|
XCTAssertFalse(VnidropError.DestinationExists(reason: "target exists").canRetryWithoutChangingInput)
|
|
XCTAssertTrue(VnidropError.Network(reason: "offline").canRetryWithoutChangingInput)
|
|
}
|
|
}
|