mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-02 21:33:26 +00:00
Hide the unread dot after previewing an invite. (#3800)
* Hide the unread dot when previewing an invite. * Remove an invited room ID when accepting/rejecting. * Remove the unread badge from knocked room cells. * Update snapshots. * Address PR comments. Refactor KnockRequestType to JoinRequestType.
This commit is contained in:
@@ -22,7 +22,7 @@ class HomeScreenRoomTests: XCTestCase {
|
||||
hasOngoingCall: Bool) {
|
||||
roomSummary = RoomSummary(roomListItem: .init(noPointer: .init()),
|
||||
id: "Test room",
|
||||
knockRequestType: nil,
|
||||
joinRequestType: nil,
|
||||
name: "Test room",
|
||||
isDirect: false,
|
||||
avatarURL: nil,
|
||||
|
||||
@@ -13,13 +13,20 @@ import XCTest
|
||||
@MainActor
|
||||
class HomeScreenViewModelTests: XCTestCase {
|
||||
var viewModel: HomeScreenViewModelProtocol!
|
||||
var clientProxy: ClientProxyMock!
|
||||
var context: HomeScreenViewModelType.Context! { viewModel.context }
|
||||
var cancellables = Set<AnyCancellable>()
|
||||
var roomSummaryProvider: RoomSummaryProviderMock!
|
||||
|
||||
override func setUpWithError() throws {
|
||||
var clientProxy: ClientProxyMock!
|
||||
var roomSummaryProvider: RoomSummaryProviderMock!
|
||||
var appSettings: AppSettings!
|
||||
|
||||
var cancellables = Set<AnyCancellable>()
|
||||
|
||||
override func setUp() {
|
||||
cancellables.removeAll()
|
||||
|
||||
AppSettings.resetAllSettings()
|
||||
appSettings = AppSettings()
|
||||
ServiceLocator.shared.register(appSettings: appSettings)
|
||||
}
|
||||
|
||||
override func tearDown() {
|
||||
@@ -29,26 +36,26 @@ class HomeScreenViewModelTests: XCTestCase {
|
||||
func testSelectRoom() async throws {
|
||||
setupViewModel()
|
||||
|
||||
let mockRoomId = "mock_room_id"
|
||||
let mockRoomID = "mock_room_id"
|
||||
var correctResult = false
|
||||
var selectedRoomId = ""
|
||||
var selectedRoomID = ""
|
||||
|
||||
viewModel.actions
|
||||
.sink { action in
|
||||
switch action {
|
||||
case .presentRoom(let roomId):
|
||||
case .presentRoom(let roomID):
|
||||
correctResult = true
|
||||
selectedRoomId = roomId
|
||||
selectedRoomID = roomID
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
|
||||
context.send(viewAction: .selectRoom(roomIdentifier: mockRoomId))
|
||||
context.send(viewAction: .selectRoom(roomIdentifier: mockRoomID))
|
||||
await Task.yield()
|
||||
XCTAssert(correctResult)
|
||||
XCTAssertEqual(mockRoomId, selectedRoomId)
|
||||
XCTAssertEqual(mockRoomID, selectedRoomID)
|
||||
}
|
||||
|
||||
func testTapUserAvatar() async throws {
|
||||
@@ -75,26 +82,26 @@ class HomeScreenViewModelTests: XCTestCase {
|
||||
func testLeaveRoomAlert() async throws {
|
||||
setupViewModel()
|
||||
|
||||
let mockRoomId = "1"
|
||||
let mockRoomID = "1"
|
||||
|
||||
clientProxy.roomForIdentifierClosure = { _ in .joined(JoinedRoomProxyMock(.init(id: mockRoomId, name: "Some room"))) }
|
||||
clientProxy.roomForIdentifierClosure = { _ in .joined(JoinedRoomProxyMock(.init(id: mockRoomID, name: "Some room"))) }
|
||||
|
||||
let deferred = deferFulfillment(context.$viewState) { value in
|
||||
value.bindings.leaveRoomAlertItem != nil
|
||||
}
|
||||
|
||||
context.send(viewAction: .leaveRoom(roomIdentifier: mockRoomId))
|
||||
context.send(viewAction: .leaveRoom(roomIdentifier: mockRoomID))
|
||||
|
||||
try await deferred.fulfill()
|
||||
|
||||
XCTAssertEqual(context.leaveRoomAlertItem?.roomID, mockRoomId)
|
||||
XCTAssertEqual(context.leaveRoomAlertItem?.roomID, mockRoomID)
|
||||
}
|
||||
|
||||
func testLeaveRoomError() async throws {
|
||||
setupViewModel()
|
||||
|
||||
let mockRoomId = "1"
|
||||
let room = JoinedRoomProxyMock(.init(id: mockRoomId, name: "Some room"))
|
||||
let mockRoomID = "1"
|
||||
let room = JoinedRoomProxyMock(.init(id: mockRoomID, name: "Some room"))
|
||||
room.leaveRoomClosure = { .failure(.sdkError(ClientProxyMockError.generic)) }
|
||||
|
||||
clientProxy.roomForIdentifierClosure = { _ in .joined(room) }
|
||||
@@ -103,7 +110,7 @@ class HomeScreenViewModelTests: XCTestCase {
|
||||
value.bindings.alertInfo != nil
|
||||
}
|
||||
|
||||
context.send(viewAction: .confirmLeaveRoom(roomIdentifier: mockRoomId))
|
||||
context.send(viewAction: .confirmLeaveRoom(roomIdentifier: mockRoomID))
|
||||
|
||||
try await deferred.fulfill()
|
||||
|
||||
@@ -113,26 +120,26 @@ class HomeScreenViewModelTests: XCTestCase {
|
||||
func testLeaveRoomSuccess() async throws {
|
||||
setupViewModel()
|
||||
|
||||
let mockRoomId = "1"
|
||||
let mockRoomID = "1"
|
||||
var correctResult = false
|
||||
let expectation = expectation(description: #function)
|
||||
viewModel.actions
|
||||
.sink { action in
|
||||
switch action {
|
||||
case .roomLeft(let roomIdentifier):
|
||||
correctResult = roomIdentifier == mockRoomId
|
||||
correctResult = roomIdentifier == mockRoomID
|
||||
default:
|
||||
break
|
||||
}
|
||||
expectation.fulfill()
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
let room = JoinedRoomProxyMock(.init(id: mockRoomId, name: "Some room"))
|
||||
let room = JoinedRoomProxyMock(.init(id: mockRoomID, name: "Some room"))
|
||||
room.leaveRoomClosure = { .success(()) }
|
||||
|
||||
clientProxy.roomForIdentifierClosure = { _ in .joined(room) }
|
||||
|
||||
context.send(viewAction: .confirmLeaveRoom(roomIdentifier: mockRoomId))
|
||||
context.send(viewAction: .confirmLeaveRoom(roomIdentifier: mockRoomID))
|
||||
await fulfillment(of: [expectation])
|
||||
XCTAssertNil(context.alertInfo)
|
||||
XCTAssertTrue(correctResult)
|
||||
@@ -141,19 +148,19 @@ class HomeScreenViewModelTests: XCTestCase {
|
||||
func testShowRoomDetails() async throws {
|
||||
setupViewModel()
|
||||
|
||||
let mockRoomId = "1"
|
||||
let mockRoomID = "1"
|
||||
var correctResult = false
|
||||
viewModel.actions
|
||||
.sink { action in
|
||||
switch action {
|
||||
case .presentRoomDetails(let roomIdentifier):
|
||||
correctResult = roomIdentifier == mockRoomId
|
||||
correctResult = roomIdentifier == mockRoomID
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
context.send(viewAction: .showRoomDetails(roomIdentifier: mockRoomId))
|
||||
context.send(viewAction: .showRoomDetails(roomIdentifier: mockRoomID))
|
||||
await Task.yield()
|
||||
XCTAssertNil(context.alertInfo)
|
||||
XCTAssertTrue(correctResult)
|
||||
@@ -256,12 +263,88 @@ class HomeScreenViewModelTests: XCTestCase {
|
||||
XCTAssertEqual(context.viewState.securityBannerMode, .none)
|
||||
}
|
||||
|
||||
func testInviteUnreadBadge() async throws {
|
||||
setupViewModel(withInvites: true)
|
||||
var invites = context.viewState.rooms.invites
|
||||
XCTAssertEqual(invites.count, 2)
|
||||
|
||||
for invite in invites {
|
||||
XCTAssertTrue(invite.badges.isDotShown)
|
||||
}
|
||||
|
||||
let deferred = deferFulfillment(context.$viewState) { state in
|
||||
state.rooms.contains { room in
|
||||
room.roomID == invites[0].roomID && room.badges.isDotShown == false
|
||||
}
|
||||
}
|
||||
appSettings.seenInvites = Set(invites.compactMap(\.roomID))
|
||||
try await deferred.fulfill()
|
||||
invites = context.viewState.rooms.invites
|
||||
|
||||
for invite in invites {
|
||||
XCTAssertFalse(invite.badges.isDotShown)
|
||||
}
|
||||
}
|
||||
|
||||
func testAcceptInvite() async throws {
|
||||
setupViewModel(withInvites: true)
|
||||
|
||||
let invitedRoomIDs = context.viewState.rooms.invites.compactMap(\.roomID)
|
||||
appSettings.seenInvites = Set(invitedRoomIDs)
|
||||
XCTAssertEqual(invitedRoomIDs.count, 2)
|
||||
|
||||
let deferred = deferFulfillment(viewModel.actions) { $0 == .presentRoom(roomIdentifier: invitedRoomIDs[0]) }
|
||||
context.send(viewAction: .acceptInvite(roomIdentifier: invitedRoomIDs[0]))
|
||||
try await deferred.fulfill()
|
||||
|
||||
XCTAssertEqual(appSettings.seenInvites, [invitedRoomIDs[1]])
|
||||
}
|
||||
|
||||
func testDeclineInvite() async throws {
|
||||
setupViewModel(withInvites: true)
|
||||
|
||||
let invitedRoomIDs = context.viewState.rooms.invites.compactMap(\.roomID)
|
||||
appSettings.seenInvites = Set(invitedRoomIDs)
|
||||
XCTAssertEqual(invitedRoomIDs.count, 2)
|
||||
|
||||
let deferred = deferFulfillment(context.$viewState) { $0.bindings.alertInfo != nil }
|
||||
context.send(viewAction: .declineInvite(roomIdentifier: invitedRoomIDs[0]))
|
||||
try await deferred.fulfill()
|
||||
|
||||
let rejectExpectation = expectation(description: "Expected rejectInvitation to be called.")
|
||||
clientProxy.roomForIdentifierClosure = { _ in
|
||||
let roomProxy = InvitedRoomProxyMock(.init())
|
||||
roomProxy.rejectInvitationClosure = {
|
||||
rejectExpectation.fulfill()
|
||||
return .success(())
|
||||
}
|
||||
|
||||
return .invited(roomProxy)
|
||||
}
|
||||
context.viewState.bindings.alertInfo?.primaryButton.action?()
|
||||
await fulfillment(of: [rejectExpectation], timeout: 1.0)
|
||||
|
||||
XCTAssertEqual(appSettings.seenInvites, [invitedRoomIDs[1]])
|
||||
}
|
||||
|
||||
// MARK: - Helpers
|
||||
|
||||
private func setupViewModel(securityStatePublisher: CurrentValuePublisher<SessionSecurityState, Never>? = nil) {
|
||||
roomSummaryProvider = RoomSummaryProviderMock(.init(state: .loaded(.mockRooms)))
|
||||
private func setupViewModel(securityStatePublisher: CurrentValuePublisher<SessionSecurityState, Never>? = nil, withInvites: Bool = false) {
|
||||
var rooms: [RoomSummary] = .mockRooms
|
||||
if withInvites {
|
||||
rooms += .mockInvites
|
||||
}
|
||||
|
||||
roomSummaryProvider = RoomSummaryProviderMock(.init(state: .loaded(rooms)))
|
||||
|
||||
clientProxy = ClientProxyMock(.init(userID: "@mock:client.com",
|
||||
roomSummaryProvider: roomSummaryProvider))
|
||||
if withInvites {
|
||||
clientProxy.joinRoomViaReturnValue = .success(())
|
||||
clientProxy.joinRoomAliasReturnValue = .success(())
|
||||
clientProxy.roomForIdentifierClosure = { _ in .invited(InvitedRoomProxyMock(.init())) }
|
||||
}
|
||||
|
||||
let userSession = UserSessionMock(.init(clientProxy: clientProxy))
|
||||
if let securityStatePublisher {
|
||||
userSession.sessionSecurityStatePublisher = securityStatePublisher
|
||||
@@ -269,8 +352,20 @@ class HomeScreenViewModelTests: XCTestCase {
|
||||
|
||||
viewModel = HomeScreenViewModel(userSession: userSession,
|
||||
analyticsService: ServiceLocator.shared.analytics,
|
||||
appSettings: ServiceLocator.shared.settings,
|
||||
appSettings: appSettings,
|
||||
selectedRoomPublisher: CurrentValueSubject<String?, Never>(nil).asCurrentValuePublisher(),
|
||||
userIndicatorController: ServiceLocator.shared.userIndicatorController)
|
||||
}
|
||||
}
|
||||
|
||||
private extension [HomeScreenRoom] {
|
||||
var invites: [HomeScreenRoom] {
|
||||
filter { room in
|
||||
if case .invite = room.type {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,12 +19,20 @@ class JoinRoomScreenViewModelTests: XCTestCase {
|
||||
}
|
||||
|
||||
var viewModel: JoinRoomScreenViewModelProtocol!
|
||||
|
||||
var clientProxy: ClientProxyMock!
|
||||
var appSettings: AppSettings!
|
||||
|
||||
var context: JoinRoomScreenViewModelType.Context {
|
||||
viewModel.context
|
||||
}
|
||||
|
||||
override func setUp() {
|
||||
AppSettings.resetAllSettings()
|
||||
appSettings = AppSettings()
|
||||
ServiceLocator.shared.register(appSettings: appSettings)
|
||||
}
|
||||
|
||||
override func tearDown() {
|
||||
viewModel = nil
|
||||
clientProxy = nil
|
||||
@@ -32,7 +40,12 @@ class JoinRoomScreenViewModelTests: XCTestCase {
|
||||
}
|
||||
|
||||
func testInteraction() async throws {
|
||||
XCTAssertTrue(appSettings.seenInvites.isEmpty, "There shouldn't be any seen invites before running the tests.")
|
||||
|
||||
setupViewModel()
|
||||
try await deferFulfillment(viewModel.context.$viewState) { $0.mode == .joinable }.fulfill()
|
||||
|
||||
XCTAssertTrue(appSettings.seenInvites.isEmpty, "Only an invited room should register the room ID as a seen invite.")
|
||||
|
||||
let deferred = deferFulfillment(viewModel.actionsPublisher) { $0 == .joined }
|
||||
context.send(viewAction: .join)
|
||||
@@ -40,34 +53,45 @@ class JoinRoomScreenViewModelTests: XCTestCase {
|
||||
}
|
||||
|
||||
func testAcceptInviteInteraction() async throws {
|
||||
setupViewModel()
|
||||
XCTAssertTrue(appSettings.seenInvites.isEmpty, "There shouldn't be any seen invites before running the tests.")
|
||||
|
||||
setupViewModel(mode: .invited)
|
||||
try await deferFulfillment(viewModel.context.$viewState) { $0.mode == .invited(isDM: false) }.fulfill()
|
||||
|
||||
XCTAssertEqual(appSettings.seenInvites, ["1"], "The invited room's ID should be registered as a seen invite.")
|
||||
|
||||
let deferred = deferFulfillment(viewModel.actionsPublisher) { $0 == .joined }
|
||||
context.send(viewAction: .acceptInvite)
|
||||
try await deferred.fulfill()
|
||||
|
||||
XCTAssertTrue(appSettings.seenInvites.isEmpty, "The after accepting an invite the invite should be forgotten in case the user leaves.")
|
||||
}
|
||||
|
||||
func testDeclineInviteInteraction() async throws {
|
||||
XCTAssertTrue(appSettings.seenInvites.isEmpty, "There shouldn't be any seen invites before running the tests.")
|
||||
|
||||
setupViewModel(mode: .invited)
|
||||
|
||||
try await deferFulfillment(viewModel.context.$viewState) { $0.roomDetails != nil }.fulfill()
|
||||
try await deferFulfillment(viewModel.context.$viewState) { $0.mode == .invited(isDM: false) }.fulfill()
|
||||
XCTAssertEqual(appSettings.seenInvites, ["1"], "The invited room's ID should be registered as a seen invite.")
|
||||
|
||||
context.send(viewAction: .declineInvite)
|
||||
|
||||
XCTAssertEqual(viewModel.context.alertInfo?.id, .declineInvite)
|
||||
let deferred = deferFulfillment(viewModel.actionsPublisher) { action in
|
||||
action == .dismiss
|
||||
}
|
||||
let deferred = deferFulfillment(viewModel.actionsPublisher) { $0 == .dismiss }
|
||||
context.alertInfo?.secondaryButton?.action?()
|
||||
try await deferred.fulfill()
|
||||
|
||||
XCTAssertTrue(appSettings.seenInvites.isEmpty, "The after declining an invite the invite should be forgotten in case another invite is received.")
|
||||
}
|
||||
|
||||
func testKnockedState() async throws {
|
||||
XCTAssertTrue(appSettings.seenInvites.isEmpty, "There shouldn't be any seen invites before running the tests.")
|
||||
setupViewModel(mode: .knocked)
|
||||
|
||||
try await deferFulfillment(viewModel.context.$viewState) { state in
|
||||
state.mode == .knocked
|
||||
}.fulfill()
|
||||
try await deferFulfillment(viewModel.context.$viewState) { $0.mode == .knocked }.fulfill()
|
||||
|
||||
XCTAssertTrue(appSettings.seenInvites.isEmpty, "Only an invited room should register the room ID as a seen invite.")
|
||||
}
|
||||
|
||||
func testCancelKnock() async throws {
|
||||
@@ -129,6 +153,7 @@ class JoinRoomScreenViewModelTests: XCTestCase {
|
||||
clientProxy = ClientProxyMock(.init())
|
||||
|
||||
clientProxy.joinRoomViaReturnValue = throwing ? .failure(.sdkError(ClientProxyMockError.generic)) : .success(())
|
||||
clientProxy.joinRoomAliasReturnValue = clientProxy.joinRoomViaReturnValue
|
||||
|
||||
switch mode {
|
||||
case .knocked:
|
||||
@@ -160,7 +185,7 @@ class JoinRoomScreenViewModelTests: XCTestCase {
|
||||
|
||||
viewModel = JoinRoomScreenViewModel(roomID: "1",
|
||||
via: [],
|
||||
appSettings: ServiceLocator.shared.settings,
|
||||
appSettings: appSettings,
|
||||
clientProxy: clientProxy,
|
||||
mediaProvider: MediaProviderMock(configuration: .init()),
|
||||
userIndicatorController: ServiceLocator.shared.userIndicatorController)
|
||||
|
||||
@@ -80,7 +80,7 @@ class LoggingTests: XCTestCase {
|
||||
let heroName = "Pseudonym"
|
||||
let roomSummary = RoomSummary(roomListItem: .init(noPointer: .init()),
|
||||
id: "myroomid",
|
||||
knockRequestType: nil,
|
||||
joinRequestType: nil,
|
||||
name: roomName,
|
||||
isDirect: true,
|
||||
avatarURL: nil,
|
||||
|
||||
@@ -56,7 +56,7 @@ class RoomSummaryTests: XCTestCase {
|
||||
func makeSummary(isDirect: Bool, hasRoomAvatar: Bool) -> RoomSummary {
|
||||
RoomSummary(roomListItem: .init(noPointer: .init()),
|
||||
id: roomDetails.id,
|
||||
knockRequestType: nil,
|
||||
joinRequestType: nil,
|
||||
name: roomDetails.name,
|
||||
isDirect: isDirect,
|
||||
avatarURL: hasRoomAvatar ? roomDetails.avatarURL : nil,
|
||||
|
||||
Reference in New Issue
Block a user