Skip to content

Commit

Permalink
chore: ios: Unit tests for recover key set and no key sets (#2317)
Browse files Browse the repository at this point in the history
  • Loading branch information
krodak authored Jan 31, 2024
1 parent 5a779bd commit 3c483a2
Show file tree
Hide file tree
Showing 5 changed files with 470 additions and 40 deletions.
27 changes: 27 additions & 0 deletions ios/PolkadotVault.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,9 @@
6D77863C2B626FD2009C8E73 /* CreateKeysForNetworksViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D77863B2B626FD2009C8E73 /* CreateKeysForNetworksViewModelTests.swift */; };
6D77863F2B62FF6F009C8E73 /* EnterKeySetNameViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D77863E2B62FF6F009C8E73 /* EnterKeySetNameViewModelTests.swift */; };
6D7786412B630116009C8E73 /* MNewSeedBackup+Generate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D7786402B630116009C8E73 /* MNewSeedBackup+Generate.swift */; };
6D7786442B630619009C8E73 /* RecoverKeySetNameViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D7786432B630619009C8E73 /* RecoverKeySetNameViewModelTests.swift */; };
6D7786462B631262009C8E73 /* CreateKeySetSeedPhraseViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D7786452B631262009C8E73 /* CreateKeySetSeedPhraseViewModelTests.swift */; };
6D7786482B631739009C8E73 /* RecoverKeySetSeedPhraseViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D7786472B631739009C8E73 /* RecoverKeySetSeedPhraseViewModelTests.swift */; };
6D77F31F296D0C5600044C7C /* CreateKeyNetworkSelectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D77F31E296D0C5600044C7C /* CreateKeyNetworkSelectionView.swift */; };
6D77F321296D0D4600044C7C /* GetManagedNetworksService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D77F320296D0D4600044C7C /* GetManagedNetworksService.swift */; };
6D77F323296D4D8900044C7C /* CreateDerivedKeyService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D77F322296D4D8900044C7C /* CreateDerivedKeyService.swift */; };
Expand Down Expand Up @@ -233,6 +235,7 @@
6DAB52EB2B5E71FC005FDBA8 /* MLog+Generate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6DAB52EA2B5E71FC005FDBA8 /* MLog+Generate.swift */; };
6DAB52ED2B5E75E7005FDBA8 /* MTransactionCardSet+Generate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6DAB52EC2B5E75E7005FDBA8 /* MTransactionCardSet+Generate.swift */; };
6DAB52EF2B5E81BB005FDBA8 /* LogNoteModalViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6DAB52EE2B5E81BB005FDBA8 /* LogNoteModalViewModelTests.swift */; };
6DAB52F22B5E832F005FDBA8 /* NoKeySetsViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6DAB52F12B5E832F005FDBA8 /* NoKeySetsViewModelTests.swift */; };
6DAFCAF82B0A360600DDD165 /* CameraPermissionHandlerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6DAFCAF72B0A360600DDD165 /* CameraPermissionHandlerTests.swift */; };
6DAFCAFA2B0AE5C000DDD165 /* KeychainAccessAdapterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6DAFCAF92B0AE5C000DDD165 /* KeychainAccessAdapterTests.swift */; };
6DAFCAFD2B0AE87300DDD165 /* RuntimePropertiesProviderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6DAFCAFC2B0AE87300DDD165 /* RuntimePropertiesProviderTests.swift */; };
Expand Down Expand Up @@ -569,7 +572,9 @@
6D77863B2B626FD2009C8E73 /* CreateKeysForNetworksViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreateKeysForNetworksViewModelTests.swift; sourceTree = "<group>"; };
6D77863E2B62FF6F009C8E73 /* EnterKeySetNameViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EnterKeySetNameViewModelTests.swift; sourceTree = "<group>"; };
6D7786402B630116009C8E73 /* MNewSeedBackup+Generate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MNewSeedBackup+Generate.swift"; sourceTree = "<group>"; };
6D7786432B630619009C8E73 /* RecoverKeySetNameViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecoverKeySetNameViewModelTests.swift; sourceTree = "<group>"; };
6D7786452B631262009C8E73 /* CreateKeySetSeedPhraseViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreateKeySetSeedPhraseViewModelTests.swift; sourceTree = "<group>"; };
6D7786472B631739009C8E73 /* RecoverKeySetSeedPhraseViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecoverKeySetSeedPhraseViewModelTests.swift; sourceTree = "<group>"; };
6D77F31E296D0C5600044C7C /* CreateKeyNetworkSelectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreateKeyNetworkSelectionView.swift; sourceTree = "<group>"; };
6D77F320296D0D4600044C7C /* GetManagedNetworksService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GetManagedNetworksService.swift; sourceTree = "<group>"; };
6D77F322296D4D8900044C7C /* CreateDerivedKeyService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreateDerivedKeyService.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -638,6 +643,7 @@
6DAB52EA2B5E71FC005FDBA8 /* MLog+Generate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MLog+Generate.swift"; sourceTree = "<group>"; };
6DAB52EC2B5E75E7005FDBA8 /* MTransactionCardSet+Generate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MTransactionCardSet+Generate.swift"; sourceTree = "<group>"; };
6DAB52EE2B5E81BB005FDBA8 /* LogNoteModalViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogNoteModalViewModelTests.swift; sourceTree = "<group>"; };
6DAB52F12B5E832F005FDBA8 /* NoKeySetsViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoKeySetsViewModelTests.swift; sourceTree = "<group>"; };
6DAFCAF72B0A360600DDD165 /* CameraPermissionHandlerTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CameraPermissionHandlerTests.swift; sourceTree = "<group>"; };
6DAFCAF92B0AE5C000DDD165 /* KeychainAccessAdapterTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeychainAccessAdapterTests.swift; sourceTree = "<group>"; };
6DAFCAFC2B0AE87300DDD165 /* RuntimePropertiesProviderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RuntimePropertiesProviderTests.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1551,6 +1557,15 @@
path = NewKeySet;
sourceTree = "<group>";
};
6D7786422B63060B009C8E73 /* RecoverKeySet */ = {
isa = PBXGroup;
children = (
6D7786432B630619009C8E73 /* RecoverKeySetNameViewModelTests.swift */,
6D7786472B631739009C8E73 /* RecoverKeySetSeedPhraseViewModelTests.swift */,
);
path = RecoverKeySet;
sourceTree = "<group>";
};
6D77F31D296D0C4000044C7C /* DerivedKey */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -1790,6 +1805,14 @@
path = Logs;
sourceTree = "<group>";
};
6DAB52F02B5E831B005FDBA8 /* NoKeySets */ = {
isa = PBXGroup;
children = (
6DAB52F12B5E832F005FDBA8 /* NoKeySetsViewModelTests.swift */,
);
path = NoKeySets;
sourceTree = "<group>";
};
6DAFCAF62B0A360600DDD165 /* Camera */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -1972,6 +1995,7 @@
isa = PBXGroup;
children = (
6D7786392B626FB4009C8E73 /* CreateKey */,
6DAB52F02B5E831B005FDBA8 /* NoKeySets */,
6DAB52E72B5E7182005FDBA8 /* Logs */,
6DB2E7BF2B4BBAEA002387DE /* Settings */,
6DDD38B52B1346C0000D2B62 /* PublicKey */,
Expand Down Expand Up @@ -2768,9 +2792,11 @@
6DB2E7CA2B4BBF6E002387DE /* MmNetwork+Generate.swift in Sources */,
6D686B9C2B45B36A007B7642 /* DevicePasscodeAuthenticatorTests.swift in Sources */,
6D80EB4B2B4E7034009C544B /* NetworkSettingDetailsActionModalViewModelTests.swift in Sources */,
6DAB52F22B5E832F005FDBA8 /* NoKeySetsViewModelTests.swift in Sources */,
6D7786462B631262009C8E73 /* CreateKeySetSeedPhraseViewModelTests.swift in Sources */,
6D57DC54289D6CE900005C63 /* NavigationTests.swift in Sources */,
6DE48E8F2B1F0B96003094D5 /* AutoMockable+Y.generated.swift in Sources */,
6D7786442B630619009C8E73 /* RecoverKeySetNameViewModelTests.swift in Sources */,
6DAFCB022B0AEE4900DDD165 /* ApplicationStatePublisherTests.swift in Sources */,
6DE48E842B1F0B96003094D5 /* AutoMockable+K.generated.swift in Sources */,
6DAB52EB2B5E71FC005FDBA8 /* MLog+Generate.swift in Sources */,
Expand All @@ -2784,6 +2810,7 @@
6DDD38B22B11C3C2000D2B62 /* SeedsMediatorTests.swift in Sources */,
6DB2E7C52B4BBC24002387DE /* BackupSelectKeyViewModelTests.swift in Sources */,
6D7786412B630116009C8E73 /* MNewSeedBackup+Generate.swift in Sources */,
6D7786482B631739009C8E73 /* RecoverKeySetSeedPhraseViewModelTests.swift in Sources */,
6DE48E812B1F0B96003094D5 /* AutoMockable+G.generated.swift in Sources */,
6D57DC52289D68B800005C63 /* ActionResult+Generate.swift in Sources */,
6DAFCAFD2B0AE87300DDD165 /* RuntimePropertiesProviderTests.swift in Sources */,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -217,16 +217,16 @@ extension RecoverKeySetSeedPhraseView {

extension RecoverKeySetSeedPhraseView {
final class ViewModel: ObservableObject {
private enum Constants {
enum Constants {
static let invisibleNonEmptyCharacter = "\u{200B}"
}

private let seedsMediator: SeedsMediating
private let textInput = TextInput()
private var shouldSkipUpdate = false
private let service: RecoverKeySetService
private let service: RecoverKeySetServicing
private let onCompletion: (CreateKeysForNetworksView.OnCompletionAction) -> Void
private let seedName: String
let seedName: String
@Binding var isPresented: Bool
@Published var isPresentingDetails: Bool = false
@Published var isValidSeedPhrase: Bool = false
Expand All @@ -235,7 +235,7 @@ extension RecoverKeySetSeedPhraseView {
@Published var guesses: [String] = []
@Published var keyboardHeight: CGFloat = 0.0

private var seedPhraseDraft: [String] = [] {
var seedPhraseDraft: [String] = [] {
didSet {
regenerateGrid()
validateSeedPhrase()
Expand All @@ -244,7 +244,7 @@ extension RecoverKeySetSeedPhraseView {
}
}

private var seedPhrase: String {
var seedPhrase: String {
seedPhraseDraft.joined(separator: " ")
}

Expand All @@ -255,7 +255,7 @@ extension RecoverKeySetSeedPhraseView {
seedName: String,
isPresented: Binding<Bool>,
seedsMediator: SeedsMediating = ServiceLocator.seedsMediator,
service: RecoverKeySetService = RecoverKeySetService(),
service: RecoverKeySetServicing = RecoverKeySetService(),
onCompletion: @escaping (CreateKeysForNetworksView.OnCompletionAction) -> Void
) {
self.seedName = seedName
Expand All @@ -274,28 +274,18 @@ extension RecoverKeySetSeedPhraseView {
seedPhraseDraft.append(guess)
}

private func updateGuesses(_ userInput: String) {
service.updateGuessWords(userInput: userInput) { result in
switch result {
case let .success(guesses):
self.guesses = guesses
case let .failure(error):
self.presentableError = .alertError(message: error.localizedDescription)
self.isPresentingError = true
}
}
}

func onUserInput(_ word: String) {
guard !shouldSkipUpdate else { return }
defer { shouldSkipUpdate = false }
shouldSkipUpdate = true
// User input is empty and invisible character was deleted
// This means that backspace was tapped, we should delete last saved word
if word.isEmpty {
seedPhraseDraft = Array(seedPhraseDraft.dropLast(1))
userInput = Constants.invisibleNonEmptyCharacter
// User added " " while typing, we should check guess words or delete whitespace
} else if word.hasSuffix(" ") {
return
}
// User added " " while typing, we should check guess words or delete whitespace
if word.hasSuffix(" ") {
let exactWord = String(word.dropFirst().dropLast(1))
// If there is a match, add this word and clear user input
if guesses.contains(exactWord) {
Expand All @@ -304,11 +294,10 @@ extension RecoverKeySetSeedPhraseView {
} else {
userInput = exactWord
}
// User just added new character, generate new guesses
} else {
updateGuesses(String(word.dropFirst()))
return
}
shouldSkipUpdate = false
// User just added new character, generate new guesses
updateGuesses(String(word.dropFirst()))
}

func onDoneTap() {
Expand All @@ -324,32 +313,39 @@ extension RecoverKeySetSeedPhraseView {
onCompletion: onCompletion
)
}
}
}

private func validateSeedPhrase() {
service.validate(seedPhrase: seedPhrase) { result in
switch result {
case let .success(isValid):
self.isValidSeedPhrase = isValid
case let .failure(error):
self.presentableError = .alertError(message: error.localizedDescription)
self.isPresentingError = true
}
private extension RecoverKeySetSeedPhraseView.ViewModel {
func updateGuesses(_ userInput: String) {
service.updateGuessWords(userInput: userInput) { result in
switch result {
case let .success(guesses):
self.guesses = guesses
case let .failure(error):
self.presentableError = .alertError(message: error.localizedDescription)
self.isPresentingError = true
}
}
}
}

private extension RecoverKeySetSeedPhraseView.ViewModel {
func regenerateGrid() {
var updatedGrid: [RecoverKeySetSeedPhraseView.GridElement] = seedPhraseDraft.enumerated()
.map { .seedPhraseElement(.init(position: String($0.offset + 1), word: $0.element)) }
updatedGrid.append(.input(textInput))
seedPhraseGrid = updatedGrid
}
}

private extension MRecoverSeedPhrase {
func draftPhrase() -> String {
draft.joined(separator: " ")
func validateSeedPhrase() {
service.validate(seedPhrase: seedPhrase) { result in
switch result {
case let .success(isValid):
self.isValidSeedPhrase = isValid
case let .failure(error):
self.isValidSeedPhrase = false
self.presentableError = .alertError(message: error.localizedDescription)
self.isPresentingError = true
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
//
// RecoverKeySetNameViewModelTests.swift
// PolkadotVaultTests
//
// Created by Krzysztof Rodak on 29/01/2024.
//

import Foundation
@testable import PolkadotVault
import SwiftUI
import XCTest

final class RecoverKeySetNameViewModelTests: XCTestCase {
private var viewModel: RecoverKeySetNameView.ViewModel!
private var seedsMediatorMock: SeedsMediatingMock!
private var isPresented: Bool = false
private var onCompletionExecuted: Bool = false

override func setUp() {
super.setUp()
seedsMediatorMock = SeedsMediatingMock()
isPresented = false
onCompletionExecuted = false
viewModel = RecoverKeySetNameView.ViewModel(
seedsMediator: seedsMediatorMock,
isPresented: Binding(get: { self.isPresented }, set: { self.isPresented = $0 }),
onCompletion: { _ in self.onCompletionExecuted = true }
)
}

override func tearDown() {
viewModel = nil
seedsMediatorMock = nil
super.tearDown()
}

func testInitialSetup() {
// Then
XCTAssertTrue(viewModel.seedName.isEmpty)
XCTAssertFalse(viewModel.isPresentingDetails)
}

func testOnBackTap() {
// When
viewModel.onBackTap()

// Then
XCTAssertFalse(isPresented)
}

func testOnNextTap() {
// When
viewModel.onNextTap()

// Then
XCTAssertTrue(viewModel.isPresentingDetails)
}

func testIsActionAvailable() {
// Given
viewModel.seedName = "ValidSeedName"
seedsMediatorMock.checkSeedCollisionSeedNameReturnValue = false

// When
let result = viewModel.isActionAvailable()

// Then
XCTAssertTrue(result)
}

func testIsActionNotAvailableDueToEmptySeedName() {
// Given
viewModel.seedName = ""

// When
let result = viewModel.isActionAvailable()

// Then
XCTAssertFalse(result)
}

func testIsActionNotAvailableDueToSeedNameCollision() {
// Given
viewModel.seedName = "CollidingSeedName"
seedsMediatorMock.checkSeedCollisionSeedNameReturnValue = true

// When
let result = viewModel.isActionAvailable()

// Then
XCTAssertFalse(result)
}

func testOnSubmitTap_ActionAvailable() {
// Given
viewModel.seedName = "ValidSeedName"
seedsMediatorMock.checkSeedCollisionSeedNameReturnValue = false

// When
viewModel.onSubmitTap()

// Then
XCTAssertTrue(viewModel.isPresentingDetails)
}

func testOnSubmitTap_ActionNotAvailable() {
// Given
viewModel.seedName = "CollidingSeedName"
seedsMediatorMock.checkSeedCollisionSeedNameReturnValue = true

// When
viewModel.onSubmitTap()

// Then
XCTAssertFalse(viewModel.isPresentingDetails)
}
}
Loading

0 comments on commit 3c483a2

Please sign in to comment.