import Combine import ComposableArchitecture import CustomDump import XCTest import XXClient import XXMessengerClient import XXModels @testable import NewGroupFeature final class NewGroupComponentTests: XCTestCase { enum Action: Equatable { case didFetchContacts(XXModels.Contact.Query) case didMakeGroup(membership: [Data], message: Data?, name: Data?) case didSaveGroup(XXModels.Group) case didSaveMessage(XXModels.Message) case didSaveGroupMember(XXModels.GroupMember) } var actions: [Action]! override func setUp() { actions = [] } override func tearDown() { actions = nil } func testStart() { let contactsSubject = PassthroughSubject<[XXModels.Contact], Error>() let store = TestStore( initialState: NewGroupComponent.State(), reducer: NewGroupComponent() ) store.dependencies.app.mainQueue = .immediate store.dependencies.app.bgQueue = .immediate store.dependencies.app.messenger.e2e.get = { var e2e: E2E = .unimplemented e2e.getContact.run = { var contact = XXClient.Contact.unimplemented("my-contact-data".data(using: .utf8)!) contact.getIdFromContact.run = { _ in "my-contact-id".data(using: .utf8)! } return contact } return e2e } store.dependencies.app.dbManager.getDB.run = { var db: Database = .unimplemented db.fetchContactsPublisher.run = { query in self.actions.append(.didFetchContacts(query)) return contactsSubject.eraseToAnyPublisher() } return db } store.send(.start) XCTAssertNoDifference(actions, [ .didFetchContacts(.init()) ]) let contacts: [XXModels.Contact] = [ .init(id: "contact-1-id".data(using: .utf8)!), .init(id: "contact-2-id".data(using: .utf8)!), .init(id: "contact-3-id".data(using: .utf8)!), ] contactsSubject.send(contacts) store.receive(.didFetchContacts(contacts)) { $0.contacts = IdentifiedArray(uniqueElements: contacts) } contactsSubject.send(completion: .finished) } func testSelectMembers() { let contacts: [XXModels.Contact] = [ .init(id: "contact-1-id".data(using: .utf8)!), .init(id: "contact-2-id".data(using: .utf8)!), .init(id: "contact-3-id".data(using: .utf8)!), ] let store = TestStore( initialState: NewGroupComponent.State( contacts: IdentifiedArray(uniqueElements: contacts) ), reducer: NewGroupComponent() ) store.send(.didSelectContact(contacts[0])) { $0.members = IdentifiedArray(uniqueElements: [contacts[0]]) } store.send(.didSelectContact(contacts[1])) { $0.members = IdentifiedArray(uniqueElements: [contacts[0], contacts[1]]) } store.send(.didSelectContact(contacts[0])) { $0.members = IdentifiedArray(uniqueElements: [contacts[1]]) } } func testEnterGroupName() { let store = TestStore( initialState: NewGroupComponent.State(), reducer: NewGroupComponent() ) store.send(.binding(.set(\.$focusedField, .name))) { $0.focusedField = .name } store.send(.binding(.set(\.$name, "My New Group"))) { $0.name = "My New Group" } store.send(.binding(.set(\.$focusedField, nil))) { $0.focusedField = nil } } func testEnterInitialMessage() { let store = TestStore( initialState: NewGroupComponent.State(), reducer: NewGroupComponent() ) store.send(.binding(.set(\.$focusedField, .message))) { $0.focusedField = .message } store.send(.binding(.set(\.$message, "Welcome message"))) { $0.message = "Welcome message" } store.send(.binding(.set(\.$focusedField, nil))) { $0.focusedField = nil } } func testCreateGroup() { let members: [XXModels.Contact] = [ .init(id: "member-contact-1".data(using: .utf8)!), .init(id: "member-contact-2".data(using: .utf8)!), .init(id: "member-contact-3".data(using: .utf8)!), ] let name = "New group" let message = "Welcome message" let groupReport = GroupReport( id: "new-group-id".data(using: .utf8)!, rounds: [], roundURL: "", status: 0 ) let myContactId = "my-contact-id".data(using: .utf8)! let currentDate = Date(timeIntervalSince1970: 123) let store = TestStore( initialState: NewGroupComponent.State( members: IdentifiedArray(uniqueElements: members), name: name, message: message ), reducer: NewGroupComponent() ) store.dependencies.app.mainQueue = .immediate store.dependencies.app.bgQueue = .immediate store.dependencies.app.messenger.groupChat.get = { var groupChat: GroupChat = .unimplemented groupChat.makeGroup.run = { membership, message, name in self.actions.append(.didMakeGroup( membership: membership, message: message, name: name )) return groupReport } return groupChat } store.dependencies.app.messenger.e2e.get = { var e2e: E2E = .unimplemented e2e.getContact.run = { var contact = XXClient.Contact.unimplemented("my-contact-data".data(using: .utf8)!) contact.getIdFromContact.run = { _ in myContactId } return contact } return e2e } store.dependencies.date = .constant(currentDate) store.dependencies.app.dbManager.getDB.run = { var db: Database = .unimplemented db.saveGroup.run = { group in self.actions.append(.didSaveGroup(group)) return group } db.saveMessage.run = { message in self.actions.append(.didSaveMessage(message)) return message } db.saveGroupMember.run = { groupMember in self.actions.append(.didSaveGroupMember(groupMember)) return groupMember } return db } store.send(.createButtonTapped) { $0.isCreating = true } XCTAssertNoDifference(actions, [ .didMakeGroup( membership: members.map(\.id), message: message.data(using: .utf8)!, name: name.data(using: .utf8)! ), .didSaveGroup(.init( id: groupReport.id, name: name, leaderId: myContactId, createdAt: currentDate, authStatus: .participating, serialized: try! groupReport.encode() )), .didSaveMessage(.init( senderId: myContactId, recipientId: nil, groupId: groupReport.id, date: currentDate, status: .sent, isUnread: false, text: message )), .didSaveGroupMember(.init( groupId: groupReport.id, contactId: members[0].id )), .didSaveGroupMember(.init( groupId: groupReport.id, contactId: members[1].id )), .didSaveGroupMember(.init( groupId: groupReport.id, contactId: members[2].id )), ]) store.receive(.didFinish) { $0.isCreating = false } } func testCreateGroupFailure() { struct Failure: Error, Equatable {} let failure = Failure() let store = TestStore( initialState: NewGroupComponent.State(), reducer: NewGroupComponent() ) store.dependencies.app.mainQueue = .immediate store.dependencies.app.bgQueue = .immediate store.dependencies.app.messenger.groupChat.get = { var groupChat: GroupChat = .unimplemented groupChat.makeGroup.run = { _, _, _ in throw failure } return groupChat } store.send(.createButtonTapped) { $0.isCreating = true } store.receive(.didFail(failure.localizedDescription)) { $0.isCreating = false $0.failure = failure.localizedDescription } } func testFinish() { let store = TestStore( initialState: NewGroupComponent.State(), reducer: NewGroupComponent() ) store.send(.didFinish) } }