Skip to content
Snippets Groups Projects
Commit 490948a0 authored by Dariusz Rybicki's avatar Dariusz Rybicki
Browse files

Merge branch 'feature/messenger-example-clean-up' into 'development'

Messenger example - clean up

See merge request elixxir/elixxir-dapps-sdk-swift!74
parents d74aeeeb d042833c
No related branches found
No related tags found
2 merge requests!102Release 1.0.0,!74Messenger example - clean up
Showing
with 233 additions and 321 deletions
import SwiftUI
import XXModels
public struct ContactAuthStatusView: View {
public init(_ authStatus: Contact.AuthStatus) {
self.authStatus = authStatus
}
public var authStatus: Contact.AuthStatus
public var body: some View {
switch authStatus {
case .stranger:
HStack {
Text("Stranger")
Spacer()
Image(systemName: "person.fill.questionmark")
}
case .requesting:
HStack {
Text("Sending auth request")
Spacer()
ProgressView()
}
case .requested:
HStack {
Text("Request sent")
Spacer()
Image(systemName: "paperplane")
}
case .requestFailed:
HStack {
Text("Sending request failed")
Spacer()
Image(systemName: "xmark.diamond.fill")
.foregroundColor(.red)
}
case .verificationInProgress:
HStack {
Text("Verification is progress")
Spacer()
ProgressView()
}
case .verified:
HStack {
Text("Verified")
Spacer()
Image(systemName: "person.fill.checkmark")
}
case .verificationFailed:
HStack {
Text("Verification failed")
Spacer()
Image(systemName: "xmark.diamond.fill")
.foregroundColor(.red)
}
case .confirming:
HStack {
Text("Confirming auth request")
Spacer()
ProgressView()
}
case .confirmationFailed:
HStack {
Text("Confirmation failed")
Spacer()
Image(systemName: "xmark.diamond.fill")
.foregroundColor(.red)
}
case .friend:
HStack {
Text("Friend")
Spacer()
Image(systemName: "person.fill.checkmark")
}
case .hidden:
HStack {
Text("Hidden")
Spacer()
Image(systemName: "eye.slash")
}
}
}
}
...@@ -53,9 +53,6 @@ extension AppEnvironment { ...@@ -53,9 +53,6 @@ extension AppEnvironment {
messenger: messenger, messenger: messenger,
mainQueue: mainQueue, mainQueue: mainQueue,
bgQueue: bgQueue, bgQueue: bgQueue,
result: {
UserSearchResultEnvironment()
},
contact: { contact: {
ContactEnvironment( ContactEnvironment(
messenger: messenger, messenger: messenger,
......
...@@ -105,13 +105,13 @@ public let contactReducer = Reducer<ContactState, ContactAction, ContactEnvironm ...@@ -105,13 +105,13 @@ public let contactReducer = Reducer<ContactState, ContactAction, ContactEnvironm
var dbContact = state.dbContact ?? XXModels.Contact(id: state.id) var dbContact = state.dbContact ?? XXModels.Contact(id: state.id)
dbContact.marshaled = xxContact.data dbContact.marshaled = xxContact.data
if state.importUsername { if state.importUsername {
dbContact.username = try? xxContact.getFact(.username)?.fact dbContact.username = try? xxContact.getFact(.username)?.value
} }
if state.importEmail { if state.importEmail {
dbContact.email = try? xxContact.getFact(.email)?.fact dbContact.email = try? xxContact.getFact(.email)?.value
} }
if state.importPhone { if state.importPhone {
dbContact.phone = try? xxContact.getFact(.phone)?.fact dbContact.phone = try? xxContact.getFact(.phone)?.value
} }
_ = try! env.db().saveContact(dbContact) _ = try! env.db().saveContact(dbContact)
} }
......
...@@ -26,9 +26,9 @@ public struct ContactView: View { ...@@ -26,9 +26,9 @@ public struct ContactView: View {
init(state: ContactState) { init(state: ContactState) {
dbContact = state.dbContact dbContact = state.dbContact
xxContactIsSet = state.xxContact != nil xxContactIsSet = state.xxContact != nil
xxContactUsername = try? state.xxContact?.getFact(.username)?.fact xxContactUsername = try? state.xxContact?.getFact(.username)?.value
xxContactEmail = try? state.xxContact?.getFact(.email)?.fact xxContactEmail = try? state.xxContact?.getFact(.email)?.value
xxContactPhone = try? state.xxContact?.getFact(.phone)?.fact xxContactPhone = try? state.xxContact?.getFact(.phone)?.value
importUsername = state.importUsername importUsername = state.importUsername
importEmail = state.importEmail importEmail = state.importEmail
importPhone = state.importPhone importPhone = state.importPhone
...@@ -79,10 +79,14 @@ public struct ContactView: View { ...@@ -79,10 +79,14 @@ public struct ContactView: View {
Button { Button {
viewStore.send(.importFactsTapped) viewStore.send(.importFactsTapped)
} label: { } label: {
if viewStore.dbContact == nil { HStack {
Text("Save contact") if viewStore.dbContact == nil {
} else { Text("Save contact")
Text("Update contact") } else {
Text("Update contact")
}
Spacer()
Image(systemName: "arrow.down")
} }
} }
} header: { } header: {
...@@ -100,87 +104,7 @@ public struct ContactView: View { ...@@ -100,87 +104,7 @@ public struct ContactView: View {
} }
Section { Section {
switch dbContact.authStatus { ContactAuthStatusView(dbContact.authStatus)
case .stranger:
HStack {
Text("Stranger")
Spacer()
Image(systemName: "person.fill.questionmark")
}
case .requesting:
HStack {
Text("Sending auth request")
Spacer()
ProgressView()
}
case .requested:
HStack {
Text("Request sent")
Spacer()
Image(systemName: "paperplane")
}
case .requestFailed:
HStack {
Text("Sending request failed")
Spacer()
Image(systemName: "xmark.diamond.fill")
.foregroundColor(.red)
}
case .verificationInProgress:
HStack {
Text("Verification is progress")
Spacer()
ProgressView()
}
case .verified:
HStack {
Text("Verified")
Spacer()
Image(systemName: "person.fill.checkmark")
}
case .verificationFailed:
HStack {
Text("Verification failed")
Spacer()
Image(systemName: "xmark.diamond.fill")
.foregroundColor(.red)
}
case .confirming:
HStack {
Text("Confirming auth request")
Spacer()
ProgressView()
}
case .confirmationFailed:
HStack {
Text("Confirmation failed")
Spacer()
Image(systemName: "xmark.diamond.fill")
.foregroundColor(.red)
}
case .friend:
HStack {
Text("Friend")
Spacer()
Image(systemName: "person.fill.checkmark")
}
case .hidden:
HStack {
Text("Hidden")
Spacer()
Image(systemName: "eye.slash")
}
}
Button { Button {
viewStore.send(.sendRequestTapped) viewStore.send(.sendRequestTapped)
} label: { } label: {
......
...@@ -200,7 +200,7 @@ public let homeReducer = Reducer<HomeState, HomeAction, HomeEnvironment> ...@@ -200,7 +200,7 @@ public let homeReducer = Reducer<HomeState, HomeAction, HomeEnvironment>
let contact = try env.dbManager.getDB().fetchContacts(.init(id: [contactId])).first let contact = try env.dbManager.getDB().fetchContacts(.init(id: [contactId])).first
if let username = contact?.username { if let username = contact?.username {
let ud = try env.messenger.ud.tryGet() let ud = try env.messenger.ud.tryGet()
try ud.permanentDeleteAccount(username: Fact(fact: username, type: 0)) try ud.permanentDeleteAccount(username: Fact(type: .username, value: username))
} }
try env.messenger.destroy() try env.messenger.destroy()
try env.dbManager.removeDB() try env.dbManager.removeDB()
......
...@@ -24,12 +24,12 @@ public struct SendRequestView: View { ...@@ -24,12 +24,12 @@ public struct SendRequestView: View {
var failure: String? var failure: String?
init(state: SendRequestState) { init(state: SendRequestState) {
contactUsername = try? state.contact.getFact(.username)?.fact contactUsername = try? state.contact.getFact(.username)?.value
contactEmail = try? state.contact.getFact(.email)?.fact contactEmail = try? state.contact.getFact(.email)?.value
contactPhone = try? state.contact.getFact(.phone)?.fact contactPhone = try? state.contact.getFact(.phone)?.value
myUsername = try? state.myContact?.getFact(.username)?.fact myUsername = try? state.myContact?.getFact(.username)?.value
myEmail = try? state.myContact?.getFact(.email)?.fact myEmail = try? state.myContact?.getFact(.email)?.value
myPhone = try? state.myContact?.getFact(.phone)?.fact myPhone = try? state.myContact?.getFact(.phone)?.value
sendUsername = state.sendUsername sendUsername = state.sendUsername
sendEmail = state.sendEmail sendEmail = state.sendEmail
sendPhone = state.sendPhone sendPhone = state.sendPhone
...@@ -134,9 +134,9 @@ public struct SendRequestView_Previews: PreviewProvider { ...@@ -134,9 +134,9 @@ public struct SendRequestView_Previews: PreviewProvider {
var contact = XXClient.Contact.unimplemented("contact-data".data(using: .utf8)!) var contact = XXClient.Contact.unimplemented("contact-data".data(using: .utf8)!)
contact.getFactsFromContact.run = { _ in contact.getFactsFromContact.run = { _ in
[ [
Fact(fact: "contact-username", type: 0), Fact(type: .username, value: "contact-username"),
Fact(fact: "contact-email", type: 1), Fact(type: .email, value: "contact-email"),
Fact(fact: "contact-phone", type: 2), Fact(type: .phone, value: "contact-phone"),
] ]
} }
return contact return contact
...@@ -145,9 +145,9 @@ public struct SendRequestView_Previews: PreviewProvider { ...@@ -145,9 +145,9 @@ public struct SendRequestView_Previews: PreviewProvider {
var contact = XXClient.Contact.unimplemented("my-data".data(using: .utf8)!) var contact = XXClient.Contact.unimplemented("my-data".data(using: .utf8)!)
contact.getFactsFromContact.run = { _ in contact.getFactsFromContact.run = { _ in
[ [
Fact(fact: "my-username", type: 0), Fact(type: .username, value: "my-username"),
Fact(fact: "my-email", type: 1), Fact(type: .email, value: "my-email"),
Fact(fact: "my-phone", type: 2), Fact(type: .phone, value: "my-phone"),
] ]
} }
return contact return contact
......
...@@ -13,12 +13,38 @@ public struct UserSearchState: Equatable { ...@@ -13,12 +13,38 @@ public struct UserSearchState: Equatable {
case phone case phone
} }
public struct Result: Equatable, Identifiable {
public init(
id: Data,
xxContact: XXClient.Contact,
username: String? = nil,
email: String? = nil,
phone: String? = nil
) {
self.id = id
self.xxContact = xxContact
self.username = username
self.email = email
self.phone = phone
}
public var id: Data
public var xxContact: XXClient.Contact
public var username: String?
public var email: String?
public var phone: String?
public var hasFacts: Bool {
username != nil || email != nil || phone != nil
}
}
public init( public init(
focusedField: Field? = nil, focusedField: Field? = nil,
query: MessengerSearchUsers.Query = .init(), query: MessengerSearchUsers.Query = .init(),
isSearching: Bool = false, isSearching: Bool = false,
failure: String? = nil, failure: String? = nil,
results: IdentifiedArrayOf<UserSearchResultState> = [], results: IdentifiedArrayOf<Result> = [],
contact: ContactState? = nil contact: ContactState? = nil
) { ) {
self.focusedField = focusedField self.focusedField = focusedField
...@@ -33,7 +59,7 @@ public struct UserSearchState: Equatable { ...@@ -33,7 +59,7 @@ public struct UserSearchState: Equatable {
@BindableState public var query: MessengerSearchUsers.Query @BindableState public var query: MessengerSearchUsers.Query
public var isSearching: Bool public var isSearching: Bool
public var failure: String? public var failure: String?
public var results: IdentifiedArrayOf<UserSearchResultState> public var results: IdentifiedArrayOf<Result>
public var contact: ContactState? public var contact: ContactState?
} }
...@@ -42,8 +68,8 @@ public enum UserSearchAction: Equatable, BindableAction { ...@@ -42,8 +68,8 @@ public enum UserSearchAction: Equatable, BindableAction {
case didFail(String) case didFail(String)
case didSucceed([Contact]) case didSucceed([Contact])
case didDismissContact case didDismissContact
case resultTapped(id: Data)
case binding(BindingAction<UserSearchState>) case binding(BindingAction<UserSearchState>)
case result(id: UserSearchResultState.ID, action: UserSearchResultAction)
case contact(ContactAction) case contact(ContactAction)
} }
...@@ -52,20 +78,17 @@ public struct UserSearchEnvironment { ...@@ -52,20 +78,17 @@ public struct UserSearchEnvironment {
messenger: Messenger, messenger: Messenger,
mainQueue: AnySchedulerOf<DispatchQueue>, mainQueue: AnySchedulerOf<DispatchQueue>,
bgQueue: AnySchedulerOf<DispatchQueue>, bgQueue: AnySchedulerOf<DispatchQueue>,
result: @escaping () -> UserSearchResultEnvironment,
contact: @escaping () -> ContactEnvironment contact: @escaping () -> ContactEnvironment
) { ) {
self.messenger = messenger self.messenger = messenger
self.mainQueue = mainQueue self.mainQueue = mainQueue
self.bgQueue = bgQueue self.bgQueue = bgQueue
self.result = result
self.contact = contact self.contact = contact
} }
public var messenger: Messenger public var messenger: Messenger
public var mainQueue: AnySchedulerOf<DispatchQueue> public var mainQueue: AnySchedulerOf<DispatchQueue>
public var bgQueue: AnySchedulerOf<DispatchQueue> public var bgQueue: AnySchedulerOf<DispatchQueue>
public var result: () -> UserSearchResultEnvironment
public var contact: () -> ContactEnvironment public var contact: () -> ContactEnvironment
} }
...@@ -75,7 +98,6 @@ extension UserSearchEnvironment { ...@@ -75,7 +98,6 @@ extension UserSearchEnvironment {
messenger: .unimplemented, messenger: .unimplemented,
mainQueue: .unimplemented, mainQueue: .unimplemented,
bgQueue: .unimplemented, bgQueue: .unimplemented,
result: { .unimplemented },
contact: { .unimplemented } contact: { .unimplemented }
) )
} }
...@@ -105,7 +127,13 @@ public let userSearchReducer = Reducer<UserSearchState, UserSearchAction, UserSe ...@@ -105,7 +127,13 @@ public let userSearchReducer = Reducer<UserSearchState, UserSearchAction, UserSe
state.failure = nil state.failure = nil
state.results = IdentifiedArray(uniqueElements: contacts.compactMap { contact in state.results = IdentifiedArray(uniqueElements: contacts.compactMap { contact in
guard let id = try? contact.getId() else { return nil } guard let id = try? contact.getId() else { return nil }
return UserSearchResultState(id: id, xxContact: contact) return UserSearchState.Result(
id: id,
xxContact: contact,
username: try? contact.getFact(.username)?.value,
email: try? contact.getFact(.email)?.value,
phone: try? contact.getFact(.phone)?.value
)
}) })
return .none return .none
...@@ -119,24 +147,18 @@ public let userSearchReducer = Reducer<UserSearchState, UserSearchAction, UserSe ...@@ -119,24 +147,18 @@ public let userSearchReducer = Reducer<UserSearchState, UserSearchAction, UserSe
state.contact = nil state.contact = nil
return .none return .none
case .result(let id, action: .tapped): case .resultTapped(let id):
state.contact = ContactState( state.contact = ContactState(
id: id, id: id,
xxContact: state.results[id: id]?.xxContact xxContact: state.results[id: id]?.xxContact
) )
return .none return .none
case .binding(_), .result(_, _), .contact(_): case .binding(_), .contact(_):
return .none return .none
} }
} }
.binding() .binding()
.presenting(
forEach: userSearchResultReducer,
state: \.results,
action: /UserSearchAction.result(id:action:),
environment: { $0.result() }
)
.presenting( .presenting(
contactReducer, contactReducer,
state: .keyPath(\.contact), state: .keyPath(\.contact),
......
import ComposableArchitecture
import Foundation
import XCTestDynamicOverlay
import XXClient
public struct UserSearchResultState: Equatable, Identifiable {
public init(
id: Data,
xxContact: XXClient.Contact,
username: String? = nil,
email: String? = nil,
phone: String? = nil
) {
self.id = id
self.xxContact = xxContact
self.username = username
self.email = email
self.phone = phone
}
public var id: Data
public var xxContact: XXClient.Contact
public var username: String?
public var email: String?
public var phone: String?
}
public enum UserSearchResultAction: Equatable {
case start
case tapped
}
public struct UserSearchResultEnvironment {
public init() {}
}
#if DEBUG
extension UserSearchResultEnvironment {
public static let unimplemented = UserSearchResultEnvironment()
}
#endif
public let userSearchResultReducer = Reducer<UserSearchResultState, UserSearchResultAction, UserSearchResultEnvironment>
{ state, action, env in
switch action {
case .start:
state.username = try? state.xxContact.getFact(.username)?.fact
state.email = try? state.xxContact.getFact(.email)?.fact
state.phone = try? state.xxContact.getFact(.phone)?.fact
return .none
case .tapped:
return .none
}
}
import ComposableArchitecture
import SwiftUI
import XXModels
public struct UserSearchResultView: View {
public init(store: Store<UserSearchResultState, UserSearchResultAction>) {
self.store = store
}
let store: Store<UserSearchResultState, UserSearchResultAction>
struct ViewState: Equatable {
var username: String?
var email: String?
var phone: String?
init(state: UserSearchResultState) {
username = state.username
email = state.email
phone = state.phone
}
var isEmpty: Bool {
username == nil && email == nil && phone == nil
}
}
public var body: some View {
WithViewStore(store.scope(state: ViewState.init)) { viewStore in
Section {
Button {
viewStore.send(.tapped)
} label: {
HStack {
VStack {
if viewStore.isEmpty {
Image(systemName: "questionmark")
.frame(maxWidth: .infinity)
} else {
if let username = viewStore.username {
Text(username)
}
if let email = viewStore.email {
Text(email)
}
if let phone = viewStore.phone {
Text(phone)
}
}
}
Spacer()
Image(systemName: "chevron.forward")
}
}
}
.task { viewStore.send(.start) }
}
}
}
#if DEBUG
public struct UserSearchResultView_Previews: PreviewProvider {
public static var previews: some View {
UserSearchResultView(store: Store(
initialState: UserSearchResultState(
id: "contact-id".data(using: .utf8)!,
xxContact: .unimplemented("contact-data".data(using: .utf8)!)
),
reducer: .empty,
environment: ()
))
}
}
#endif
...@@ -17,12 +17,14 @@ public struct UserSearchView: View { ...@@ -17,12 +17,14 @@ public struct UserSearchView: View {
var query: MessengerSearchUsers.Query var query: MessengerSearchUsers.Query
var isSearching: Bool var isSearching: Bool
var failure: String? var failure: String?
var results: IdentifiedArrayOf<UserSearchState.Result>
init(state: UserSearchState) { init(state: UserSearchState) {
focusedField = state.focusedField focusedField = state.focusedField
query = state.query query = state.query
isSearching = state.isSearching isSearching = state.isSearching
failure = state.failure failure = state.failure
results = state.results
} }
} }
...@@ -87,13 +89,34 @@ public struct UserSearchView: View { ...@@ -87,13 +89,34 @@ public struct UserSearchView: View {
} }
} }
ForEachStore( ForEach(viewStore.results) { result in
store.scope( Section {
state: \.results, Button {
action: UserSearchAction.result(id:action:) viewStore.send(.resultTapped(id: result.id))
), } label: {
content: UserSearchResultView.init(store:) HStack {
) VStack {
if result.hasFacts {
if let username = result.username {
Label(username, systemImage: "person")
}
if let email = result.email {
Label(email, systemImage: "envelope")
}
if let phone = result.phone {
Label(phone, systemImage: "phone")
}
} else {
Label("No facts", systemImage: "questionmark")
}
}
.tint(Color.primary)
Spacer()
Image(systemName: "chevron.forward")
}
}
}
}
} }
.onChange(of: viewStore.focusedField) { focusedField = $0 } .onChange(of: viewStore.focusedField) { focusedField = $0 }
.onChange(of: focusedField) { viewStore.send(.set(\.$focusedField, $0)) } .onChange(of: focusedField) { viewStore.send(.set(\.$focusedField, $0)) }
......
...@@ -55,9 +55,9 @@ final class ContactFeatureTests: XCTestCase { ...@@ -55,9 +55,9 @@ final class ContactFeatureTests: XCTestCase {
var xxContact: XXClient.Contact = .unimplemented("contact-data".data(using: .utf8)!) var xxContact: XXClient.Contact = .unimplemented("contact-data".data(using: .utf8)!)
xxContact.getFactsFromContact.run = { _ in xxContact.getFactsFromContact.run = { _ in
[ [
Fact(fact: "contact-username", type: 0), Fact(type: .username, value: "contact-username"),
Fact(fact: "contact-email", type: 1), Fact(type: .email, value: "contact-email"),
Fact(fact: "contact-phone", type: 2), Fact(type: .phone, value: "contact-phone"),
] ]
} }
......
...@@ -370,7 +370,7 @@ final class HomeFeatureTests: XCTestCase { ...@@ -370,7 +370,7 @@ final class HomeFeatureTests: XCTestCase {
} }
XCTAssertNoDifference(dbDidFetchContacts, [.init(id: ["contact-id".data(using: .utf8)!])]) XCTAssertNoDifference(dbDidFetchContacts, [.init(id: ["contact-id".data(using: .utf8)!])])
XCTAssertNoDifference(udDidPermanentDeleteAccount, [Fact(fact: "MyUsername", type: 0)]) XCTAssertNoDifference(udDidPermanentDeleteAccount, [Fact(type: .username, value: "MyUsername")])
XCTAssertNoDifference(messengerDidDestroy, 1) XCTAssertNoDifference(messengerDidDestroy, 1)
XCTAssertNoDifference(didRemoveDB, 1) XCTAssertNoDifference(didRemoveDB, 1)
......
...@@ -63,7 +63,7 @@ final class RegisterFeatureTests: XCTestCase { ...@@ -63,7 +63,7 @@ final class RegisterFeatureTests: XCTestCase {
bgQueue.advance() bgQueue.advance()
XCTAssertNoDifference(messengerDidRegisterUsername, ["NewUser"]) XCTAssertNoDifference(messengerDidRegisterUsername, ["NewUser"])
XCTAssertNoDifference(didSetFactsOnContact, [[Fact(fact: "NewUser", type: 0)]]) XCTAssertNoDifference(didSetFactsOnContact, [[Fact(type: .username, value: "NewUser")]])
XCTAssertNoDifference(dbDidSaveContact, [ XCTAssertNoDifference(dbDidSaveContact, [
XXModels.Contact( XXModels.Contact(
id: "contact-id".data(using: .utf8)!, id: "contact-id".data(using: .utf8)!,
......
...@@ -63,9 +63,9 @@ final class SendRequestFeatureTests: XCTestCase { ...@@ -63,9 +63,9 @@ final class SendRequestFeatureTests: XCTestCase {
var myContact: XXClient.Contact = .unimplemented("my-contact-data".data(using: .utf8)!) var myContact: XXClient.Contact = .unimplemented("my-contact-data".data(using: .utf8)!)
let myFacts = [ let myFacts = [
Fact(fact: "my-username", type: 0), Fact(type: .username, value: "my-username"),
Fact(fact: "my-email", type: 1), Fact(type: .email, value: "my-email"),
Fact(fact: "my-phone", type: 2), Fact(type: .phone, value: "my-phone"),
] ]
myContact.getFactsFromContact.run = { _ in myFacts } myContact.getFactsFromContact.run = { _ in myFacts }
...@@ -142,9 +142,9 @@ final class SendRequestFeatureTests: XCTestCase { ...@@ -142,9 +142,9 @@ final class SendRequestFeatureTests: XCTestCase {
var myContact: XXClient.Contact = .unimplemented("my-contact-data".data(using: .utf8)!) var myContact: XXClient.Contact = .unimplemented("my-contact-data".data(using: .utf8)!)
let myFacts = [ let myFacts = [
Fact(fact: "my-username", type: 0), Fact(type: .username, value: "my-username"),
Fact(fact: "my-email", type: 1), Fact(type: .email, value: "my-email"),
Fact(fact: "my-phone", type: 2), Fact(type: .phone, value: "my-phone"),
] ]
myContact.getFactsFromContact.run = { _ in myFacts } myContact.getFactsFromContact.run = { _ in myFacts }
......
...@@ -20,12 +20,26 @@ final class UserSearchFeatureTests: XCTestCase { ...@@ -20,12 +20,26 @@ final class UserSearchFeatureTests: XCTestCase {
var contact1 = Contact.unimplemented("contact-1".data(using: .utf8)!) var contact1 = Contact.unimplemented("contact-1".data(using: .utf8)!)
contact1.getIdFromContact.run = { _ in "contact-1-id".data(using: .utf8)! } contact1.getIdFromContact.run = { _ in "contact-1-id".data(using: .utf8)! }
var contact2 = Contact.unimplemented("contact-1".data(using: .utf8)!) contact1.getFactsFromContact.run = { _ in
[
Fact(type: .username, value: "contact-1-username"),
Fact(type: .email, value: "contact-1-email"),
Fact(type: .phone, value: "contact-1-phone"),
]
}
var contact2 = Contact.unimplemented("contact-2".data(using: .utf8)!)
contact2.getIdFromContact.run = { _ in "contact-2-id".data(using: .utf8)! } contact2.getIdFromContact.run = { _ in "contact-2-id".data(using: .utf8)! }
contact2.getFactsFromContact.run = { _ in
[
Fact(type: .username, value: "contact-2-username"),
]
}
var contact3 = Contact.unimplemented("contact-3".data(using: .utf8)!) var contact3 = Contact.unimplemented("contact-3".data(using: .utf8)!)
contact3.getIdFromContact.run = { _ in throw GetIdFromContactError() } contact3.getIdFromContact.run = { _ in throw GetIdFromContactError() }
contact3.getFactsFromContact.run = { _ in [] }
var contact4 = Contact.unimplemented("contact-4".data(using: .utf8)!) var contact4 = Contact.unimplemented("contact-4".data(using: .utf8)!)
contact4.getIdFromContact.run = { _ in "contact-4-id".data(using: .utf8)! } contact4.getIdFromContact.run = { _ in "contact-4-id".data(using: .utf8)! }
contact4.getFactsFromContact.run = { _ in throw GetFactsFromContactError() }
let contacts = [contact1, contact2, contact3, contact4] let contacts = [contact1, contact2, contact3, contact4]
store.environment.bgQueue = .immediate store.environment.bgQueue = .immediate
...@@ -54,9 +68,22 @@ final class UserSearchFeatureTests: XCTestCase { ...@@ -54,9 +68,22 @@ final class UserSearchFeatureTests: XCTestCase {
$0.isSearching = false $0.isSearching = false
$0.failure = nil $0.failure = nil
$0.results = [ $0.results = [
.init(id: "contact-1-id".data(using: .utf8)!, xxContact: contact1), .init(
.init(id: "contact-2-id".data(using: .utf8)!, xxContact: contact2), id: "contact-1-id".data(using: .utf8)!,
.init(id: "contact-4-id".data(using: .utf8)!, xxContact: contact4) xxContact: contact1,
username: "contact-1-username",
email: "contact-1-email",
phone: "contact-1-phone"
),
.init(
id: "contact-2-id".data(using: .utf8)!,
xxContact: contact2,
username: "contact-2-username"
),
.init(
id: "contact-4-id".data(using: .utf8)!,
xxContact: contact4
)
] ]
} }
} }
...@@ -103,7 +130,7 @@ final class UserSearchFeatureTests: XCTestCase { ...@@ -103,7 +130,7 @@ final class UserSearchFeatureTests: XCTestCase {
environment: .unimplemented environment: .unimplemented
) )
store.send(.result(id: "contact-id".data(using: .utf8)!, action: .tapped)) { store.send(.resultTapped(id: "contact-id".data(using: .utf8)!)) {
$0.contact = ContactState( $0.contact = ContactState(
id: "contact-id".data(using: .utf8)!, id: "contact-id".data(using: .utf8)!,
xxContact: .unimplemented("contact-data".data(using: .utf8)!) xxContact: .unimplemented("contact-data".data(using: .utf8)!)
......
import ComposableArchitecture
import XCTest
import XCTestDynamicOverlay
import XXClient
@testable import UserSearchFeature
final class UserSearchResultFeatureTests: XCTestCase {
func testStart() {
var contact = Contact.unimplemented("contact-data".data(using: .utf8)!)
contact.getFactsFromContact.run = { _ in
[
Fact(fact: "contact-username", type: 0),
Fact(fact: "contact-email", type: 1),
Fact(fact: "contact-phone", type: 2),
]
}
let store = TestStore(
initialState: UserSearchResultState(
id: "contact-id".data(using: .utf8)!,
xxContact: contact
),
reducer: userSearchResultReducer,
environment: .unimplemented
)
store.send(.start) {
$0.username = "contact-username"
$0.email = "contact-email"
$0.phone = "contact-phone"
}
}
func testTapped() {
let store = TestStore(
initialState: UserSearchResultState(
id: "contact-id".data(using: .utf8)!,
xxContact: .unimplemented("contact-data".data(using: .utf8)!)
),
reducer: userSearchResultReducer,
environment: .unimplemented
)
store.send(.tapped)
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment