mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-02 21:33:26 +00:00
Redaction reason (#6098)
This commit is contained in:
@@ -198,17 +198,44 @@ struct PollFormScreenViewModelTests {
|
||||
|
||||
try await deferredFailure.fulfill()
|
||||
#expect(context.alertInfo != nil, "An alert should be shown before deleting the poll.")
|
||||
#expect(context.alertInfo?.textFields?.count == 1, "The alert should let the user give a reason.")
|
||||
|
||||
let deferred = deferFulfillment(viewModel.actions) { $0 == .close }
|
||||
|
||||
await waitForConfirmation(timeout: .seconds(1)) { confirmation in
|
||||
timelineProxy.redactReasonClosure = { eventID, _ in
|
||||
timelineProxy.redactReasonClosure = { eventID, reason in
|
||||
defer {
|
||||
confirmation()
|
||||
}
|
||||
#expect(eventID == .eventID("foo"))
|
||||
#expect(reason == nil, "A blank reason shouldn't be sent.")
|
||||
return .success(())
|
||||
}
|
||||
context.alertInfo?.textFields?.first?.text.wrappedValue = " "
|
||||
context.alertInfo?.secondaryButton?.action?()
|
||||
}
|
||||
try await deferred.fulfill()
|
||||
}
|
||||
|
||||
@Test
|
||||
mutating func deletePollWithReason() async throws {
|
||||
setupViewModel(mode: .edit(eventID: "foo", poll: .emptyDisclosed))
|
||||
|
||||
context.send(viewAction: .delete)
|
||||
#expect(context.alertInfo != nil, "An alert should be shown before deleting the poll.")
|
||||
|
||||
let deferred = deferFulfillment(viewModel.actions) { $0 == .close }
|
||||
|
||||
await waitForConfirmation(timeout: .seconds(1)) { confirmation in
|
||||
timelineProxy.redactReasonClosure = { eventID, reason in
|
||||
defer {
|
||||
confirmation()
|
||||
}
|
||||
#expect(eventID == .eventID("foo"))
|
||||
#expect(reason == "Posted in the wrong room.")
|
||||
return .success(())
|
||||
}
|
||||
context.alertInfo?.textFields?.first?.text.wrappedValue = "Posted in the wrong room."
|
||||
context.alertInfo?.secondaryButton?.action?()
|
||||
}
|
||||
try await deferred.fulfill()
|
||||
|
||||
@@ -154,7 +154,7 @@ struct TimelineMediaPreviewViewModelTests {
|
||||
// Given a view model with a loaded item.
|
||||
try await loadingItem()
|
||||
#expect(context.redactConfirmationItem == nil)
|
||||
#expect(!timelineController.redactCalled)
|
||||
#expect(!timelineController.redactReasonCalled)
|
||||
guard case let .media(mediaItem) = context.viewState.currentItem else {
|
||||
Issue.record("There should be a current item.")
|
||||
return
|
||||
@@ -177,18 +177,19 @@ struct TimelineMediaPreviewViewModelTests {
|
||||
|
||||
// Then the confirmation sheet should be presented.
|
||||
#expect(context.redactConfirmationItem == mediaItem)
|
||||
#expect(!timelineController.redactCalled)
|
||||
#expect(!timelineController.redactReasonCalled)
|
||||
|
||||
// When confirming the redaction.
|
||||
let deferred = deferFulfillment(viewModel.actions) { $0 == .dismiss }
|
||||
|
||||
// The redaction runs in an unstructured task, so wait for the call rather than asserting after the dismiss.
|
||||
await waitForConfirmation { confirmation in
|
||||
timelineController.redactClosure = { _ in confirmation() }
|
||||
context.send(viewAction: .redactConfirmation(item: mediaItem))
|
||||
timelineController.redactReasonClosure = { _, _ in confirmation() }
|
||||
context.send(viewAction: .redactConfirmation(item: mediaItem, reason: "Wrong file"))
|
||||
}
|
||||
|
||||
// Then the item should be redacted and the view should be dismissed.
|
||||
// Then the item should be redacted with the reason and the view should be dismissed.
|
||||
#expect(timelineController.redactReasonReceivedArguments?.reason == "Wrong file")
|
||||
try await deferred.fulfill()
|
||||
}
|
||||
|
||||
|
||||
@@ -594,6 +594,99 @@ final class TimelineViewModelTests {
|
||||
#expect(viewModel.state.bindings.alertInfo?.verticalButtons == nil)
|
||||
}
|
||||
|
||||
// MARK: - Redaction
|
||||
|
||||
@Test
|
||||
func redactionAsksForConfirmation() async throws {
|
||||
// Given a timeline containing a message that has been sent.
|
||||
let item = TextRoomTimelineItem(text: "Hello", sender: "bob")
|
||||
let timelineController = TimelineControllerMock(.init(timelineItems: [item]))
|
||||
let viewModel = makeViewModel(timelineController: timelineController)
|
||||
|
||||
// When choosing to remove it.
|
||||
let deferred = deferFulfillment(viewModel.context.$viewState) { $0.bindings.redactConfirmationInfo?.id == item.id }
|
||||
viewModel.context.send(viewAction: .handleTimelineItemMenuAction(itemID: item.id, action: .redact(isMedia: false)))
|
||||
|
||||
// Then a confirmation should be shown without anything being redacted yet.
|
||||
try await deferred.fulfill()
|
||||
#expect(!timelineController.redactReasonCalled)
|
||||
}
|
||||
|
||||
@Test
|
||||
func redactionSendsTheReason() async throws {
|
||||
// Given a timeline showing a redaction confirmation.
|
||||
let item = TextRoomTimelineItem(text: "Hello", sender: "bob")
|
||||
let timelineController = TimelineControllerMock(.init(timelineItems: [item]))
|
||||
let viewModel = makeViewModel(timelineController: timelineController)
|
||||
let deferredConfirmation = deferFulfillment(viewModel.context.$viewState) { $0.bindings.redactConfirmationInfo != nil }
|
||||
viewModel.context.send(viewAction: .handleTimelineItemMenuAction(itemID: item.id, action: .redact(isMedia: false)))
|
||||
try await deferredConfirmation.fulfill()
|
||||
|
||||
// When confirming the removal with a reason.
|
||||
// The redaction runs in an unstructured task, so wait for the call rather than asserting straight after.
|
||||
await waitForConfirmation { confirmation in
|
||||
timelineController.redactReasonClosure = { _, _ in confirmation() }
|
||||
viewModel.context.send(viewAction: .redactConfirmed(itemID: item.id, reason: "Posted in the wrong room"))
|
||||
}
|
||||
|
||||
// Then the reason should be sent with the redaction and the confirmation dismissed.
|
||||
#expect(timelineController.redactReasonReceivedArguments?.reason == "Posted in the wrong room")
|
||||
#expect(viewModel.context.redactConfirmationInfo == nil)
|
||||
}
|
||||
|
||||
@Test
|
||||
func redactionIgnoresABlankReason() async {
|
||||
// Given a timeline containing a message that has been sent.
|
||||
let item = TextRoomTimelineItem(text: "Hello", sender: "bob")
|
||||
let timelineController = TimelineControllerMock(.init(timelineItems: [item]))
|
||||
let viewModel = makeViewModel(timelineController: timelineController)
|
||||
|
||||
// When confirming the removal without typing a reason.
|
||||
await waitForConfirmation { confirmation in
|
||||
timelineController.redactReasonClosure = { _, _ in confirmation() }
|
||||
viewModel.context.send(viewAction: .redactConfirmed(itemID: item.id, reason: " "))
|
||||
}
|
||||
|
||||
// Then no reason should be sent, behaving exactly as it did before the confirmation existed.
|
||||
#expect(timelineController.redactReasonReceivedArguments?.reason == nil)
|
||||
}
|
||||
|
||||
@Test
|
||||
func redactionCanBeCancelled() async throws {
|
||||
// Given a timeline showing a redaction confirmation.
|
||||
let item = TextRoomTimelineItem(text: "Hello", sender: "bob")
|
||||
let timelineController = TimelineControllerMock(.init(timelineItems: [item]))
|
||||
let viewModel = makeViewModel(timelineController: timelineController)
|
||||
let deferred = deferFulfillment(viewModel.context.$viewState) { $0.bindings.redactConfirmationInfo != nil }
|
||||
viewModel.context.send(viewAction: .handleTimelineItemMenuAction(itemID: item.id, action: .redact(isMedia: false)))
|
||||
try await deferred.fulfill()
|
||||
|
||||
// When dismissing the sheet without confirming.
|
||||
viewModel.context.redactConfirmationInfo = nil
|
||||
|
||||
// Then nothing should be redacted.
|
||||
#expect(!timelineController.redactReasonCalled)
|
||||
}
|
||||
|
||||
@Test
|
||||
func redactionOfAnUnsentMessageSkipsTheConfirmation() async {
|
||||
// Given a message that was never sent, so has nobody to give a reason to.
|
||||
let timelineController = TimelineControllerMock(.init(timelineItems: []))
|
||||
let viewModel = makeViewModel(timelineController: timelineController)
|
||||
let itemID = TimelineItemIdentifier.event(uniqueID: .init(UUID().uuidString),
|
||||
eventOrTransactionID: .transactionID(UUID().uuidString))
|
||||
|
||||
// When choosing to remove it.
|
||||
await waitForConfirmation { confirmation in
|
||||
timelineController.redactReasonClosure = { _, _ in confirmation() }
|
||||
viewModel.context.send(viewAction: .handleTimelineItemMenuAction(itemID: itemID, action: .redact(isMedia: false)))
|
||||
}
|
||||
|
||||
// Then it should be redacted immediately, with no reason and no confirmation.
|
||||
#expect(viewModel.context.redactConfirmationInfo == nil)
|
||||
#expect(timelineController.redactReasonReceivedArguments?.reason == nil)
|
||||
}
|
||||
|
||||
// MARK: - Helpers
|
||||
|
||||
private func makeViewModel(roomProxy: JoinedRoomProxyProtocol? = nil,
|
||||
|
||||
Reference in New Issue
Block a user