Files
vnidrop/apple/Tests/ReceiveModelTests.swift
cdricms 6180cb95ce fix(apple): stop delete confirmation re-presenting on macOS
Confirming a transfer/history deletion flashed the same confirmation alert a
second time before it went away. The destructive button runs confirmDelete
synchronously (setting isDeleting = true), while the alert's isPresented
dismiss binding fires asynchronously and then no-ops because its
`if !isDeleting` guard is already false — leaving the open flag set, so macOS
re-reads the binding as true and re-presents the alert until the async delete
finally clears it.

Close the confirmation flag synchronously in confirmDeleteTransfer /
confirmHistoryDelete so there's no window for re-presentation. Tests assert
the flag clears immediately, before the async delete completes.
2026-07-23 23:43:05 +02:00

71 lines
2.6 KiB
Swift

import XCTest
@testable import VniDrop
/// Ports the receive-side state-machine assertions from `feature/ViewModelsTest.kt`.
@MainActor
final class ReceiveModelTests: XCTestCase {
private func makeModel(_ core: FakeCoreGateway) -> ReceiveModel {
ReceiveModel(
repository: core,
fileSystemService: FakeFileSystemService(),
preferences: Fixtures.preferences(),
messages: UiMessageController()
)
}
func testDeleteHistoryItemDeletesTerminalReceiveTransfer() async {
let core = FakeCoreGateway()
let model = makeModel(core)
core.setState(CoreState(isInitialized: true, transfers: [Fixtures.transfer(id: 5, direction: .receive, status: .done)]))
await waitUntil { model.coreState.transfers.contains { $0.transferId == 5 } }
model.requestDeleteHistoryItem(5)
XCTAssertEqual(model.state.historyDeleteTarget, .transfer(transferId: 5))
model.confirmHistoryDelete()
// Must close immediately (not after the async delete) so the alert can't
// re-present on macOS.
XCTAssertNil(model.state.historyDeleteTarget)
await waitUntil { core.deletedTransfers.contains(5) }
XCTAssertEqual(core.deletedTransfers, [5])
XCTAssertNil(model.state.historyDeleteTarget)
}
func testClearHistoryCallsClearReceiveHistory() async {
let core = FakeCoreGateway()
let model = makeModel(core)
core.setState(CoreState(isInitialized: true, transfers: [Fixtures.transfer(id: 5, direction: .receive, status: .done)]))
await waitUntil { !model.coreState.transfers.isEmpty }
model.requestClearHistory()
XCTAssertEqual(model.state.historyDeleteTarget, .all)
model.confirmHistoryDelete()
await waitUntil { core.clearReceiveHistoryCount == 1 }
XCTAssertEqual(core.clearReceiveHistoryCount, 1)
XCTAssertNil(model.state.historyDeleteTarget)
}
func testDeleteHistoryItemIgnoresNonTerminalTransfer() async {
let core = FakeCoreGateway()
let model = makeModel(core)
core.setState(CoreState(isInitialized: true, transfers: [Fixtures.transfer(id: 9, direction: .receive, status: .receiving)]))
await waitUntil { !model.coreState.transfers.isEmpty }
model.requestDeleteHistoryItem(9)
XCTAssertNil(model.state.historyDeleteTarget) // in-flight receive can't be deleted from history
}
func testCancelActiveReceiveCancelsTheReceivingTransfer() async {
let core = FakeCoreGateway()
let model = makeModel(core)
core.setState(CoreState(isInitialized: true, transfers: [Fixtures.transfer(id: 7, direction: .receive, status: .receiving)]))
await waitUntil { !model.coreState.transfers.isEmpty }
model.cancelActiveReceive()
await waitUntil { core.cancelledTransfers.contains(7) }
XCTAssertEqual(core.cancelledTransfers, [7])
}
}