Skip to content

Commit

Permalink
chore: ios: Add unit tests for Sign Specs and Settings Backup Modal (#…
Browse files Browse the repository at this point in the history
…2303)

Co-authored-by: Pavel Rybalko <pavel@parity.io>
  • Loading branch information
krodak and prybalko authored Jan 22, 2024
1 parent 84b6419 commit c39dd48
Show file tree
Hide file tree
Showing 6 changed files with 307 additions and 50 deletions.
16 changes: 10 additions & 6 deletions ios/PolkadotVault.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,9 @@
6D2779C828B3D33100570055 /* NavigationBarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D2779C728B3D33100570055 /* NavigationBarView.swift */; };
6D2A5D0F2AA5DF47009E0C3A /* ManageKeySetsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D2A5D0E2AA5DF47009E0C3A /* ManageKeySetsView.swift */; };
6D2A5D112AA607C7009E0C3A /* ActionSheetCircleButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D2A5D102AA607C7009E0C3A /* ActionSheetCircleButton.swift */; };
6D2C78AC2B56D98F006431E3 /* SignSpecDetailsViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D2C78AB2B56D98F006431E3 /* SignSpecDetailsViewModelTests.swift */; };
6D2C78AE2B56DB07006431E3 /* SignSpecEnterPasswordModalViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D2C78AD2B56DB07006431E3 /* SignSpecEnterPasswordModalViewModelTests.swift */; };
6D2C78B02B56EF55006431E3 /* SettingsBackupModalViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D2C78AF2B56EF55006431E3 /* SettingsBackupModalViewModelTests.swift */; };
6D2D244B28CCAD4100862726 /* ExportPrivateKeyService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D2D244A28CCAD4100862726 /* ExportPrivateKeyService.swift */; };
6D2D244D28CE55A000862726 /* String+Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D2D244C28CE55A000862726 /* String+Utilities.swift */; };
6D2D244F28CE5C1B00862726 /* NavbarActionButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D2D244E28CE5C1B00862726 /* NavbarActionButton.swift */; };
Expand Down Expand Up @@ -169,9 +172,6 @@
6D8045D928D0761E00237F8C /* QRCodeAddressFooterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D8045D828D0761E00237F8C /* QRCodeAddressFooterView.swift */; };
6D8045DC28D0840F00237F8C /* MKeyDetails+Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D8045DB28D0840F00237F8C /* MKeyDetails+Helpers.swift */; };
6D8045DE28D087D400237F8C /* QRCodeRootFooterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D8045DD28D087D400237F8C /* QRCodeRootFooterView.swift */; };
6D80EB4B2B4E7034009C544B /* NetworkSettingDetailsActionModalViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D80EB4A2B4E7034009C544B /* NetworkSettingDetailsActionModalViewModelTests.swift */; };
6D80EB522B4EB0B8009C544B /* MSufficientCryptoReady+Generate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D80EB512B4EB0B8009C544B /* MSufficientCryptoReady+Generate.swift */; };
6D80EB542B4EB0C8009C544B /* MAddressCard+Generate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D80EB532B4EB0C8009C544B /* MAddressCard+Generate.swift */; };
6D80EB492B4CF582009C544B /* BananaSplitService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D80EB482B4CF582009C544B /* BananaSplitService.swift */; };
6D80EB4B2B4E7034009C544B /* NetworkSettingDetailsActionModalViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D80EB4A2B4E7034009C544B /* NetworkSettingDetailsActionModalViewModelTests.swift */; };
6D80EB502B4EAD3E009C544B /* VerifierCertificateViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D80EB4F2B4EAD3E009C544B /* VerifierCertificateViewModelTests.swift */; };
Expand Down Expand Up @@ -476,6 +476,9 @@
6D2779C728B3D33100570055 /* NavigationBarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationBarView.swift; sourceTree = "<group>"; };
6D2A5D0E2AA5DF47009E0C3A /* ManageKeySetsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ManageKeySetsView.swift; sourceTree = "<group>"; };
6D2A5D102AA607C7009E0C3A /* ActionSheetCircleButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionSheetCircleButton.swift; sourceTree = "<group>"; };
6D2C78AB2B56D98F006431E3 /* SignSpecDetailsViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignSpecDetailsViewModelTests.swift; sourceTree = "<group>"; };
6D2C78AD2B56DB07006431E3 /* SignSpecEnterPasswordModalViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignSpecEnterPasswordModalViewModelTests.swift; sourceTree = "<group>"; };
6D2C78AF2B56EF55006431E3 /* SettingsBackupModalViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsBackupModalViewModelTests.swift; sourceTree = "<group>"; };
6D2D244A28CCAD4100862726 /* ExportPrivateKeyService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExportPrivateKeyService.swift; sourceTree = "<group>"; };
6D2D244C28CE55A000862726 /* String+Utilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+Utilities.swift"; sourceTree = "<group>"; };
6D2D244E28CE5C1B00862726 /* NavbarActionButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavbarActionButton.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -567,9 +570,6 @@
6D8045D828D0761E00237F8C /* QRCodeAddressFooterView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QRCodeAddressFooterView.swift; sourceTree = "<group>"; };
6D8045DB28D0840F00237F8C /* MKeyDetails+Helpers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MKeyDetails+Helpers.swift"; sourceTree = "<group>"; };
6D8045DD28D087D400237F8C /* QRCodeRootFooterView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = QRCodeRootFooterView.swift; sourceTree = "<group>"; };
6D80EB4A2B4E7034009C544B /* NetworkSettingDetailsActionModalViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkSettingDetailsActionModalViewModelTests.swift; sourceTree = "<group>"; };
6D80EB512B4EB0B8009C544B /* MSufficientCryptoReady+Generate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MSufficientCryptoReady+Generate.swift"; sourceTree = "<group>"; };
6D80EB532B4EB0C8009C544B /* MAddressCard+Generate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MAddressCard+Generate.swift"; sourceTree = "<group>"; };
6D80EB482B4CF582009C544B /* BananaSplitService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BananaSplitService.swift; sourceTree = "<group>"; };
6D80EB4A2B4E7034009C544B /* NetworkSettingDetailsActionModalViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkSettingDetailsActionModalViewModelTests.swift; sourceTree = "<group>"; };
6D80EB4F2B4EAD3E009C544B /* VerifierCertificateViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VerifierCertificateViewModelTests.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1774,6 +1774,7 @@
isa = PBXGroup;
children = (
6DB2E7C42B4BBC24002387DE /* BackupSelectKeyViewModelTests.swift */,
6D2C78AF2B56EF55006431E3 /* SettingsBackupModalViewModelTests.swift */,
);
path = Backup;
sourceTree = "<group>";
Expand Down Expand Up @@ -2696,11 +2697,13 @@
6DE48E912B1F0B96003094D5 /* AutoMockable+H.generated.swift in Sources */,
6DAFCB002B0AEB7E00DDD165 /* ConnectivityMediatorTests.swift in Sources */,
6DE48E8E2B1F0B96003094D5 /* AutoMockable+B.generated.swift in Sources */,
6D2C78AC2B56D98F006431E3 /* SignSpecDetailsViewModelTests.swift in Sources */,
6DE48E992B1F14A0003094D5 /* QRCodeAddressFooterViewModel+Generate.swift in Sources */,
6DE48E832B1F0B96003094D5 /* AutoMockable+C.generated.swift in Sources */,
6D80EB542B4EB0C8009C544B /* MAddressCard+Generate.swift in Sources */,
6D8AF88228BCC4D100CF0AB2 /* AccessControlProvidingAssemblerTests.swift in Sources */,
6DE1B8322B56C54900D299C1 /* CancelBagTests.swift in Sources */,
6D2C78AE2B56DB07006431E3 /* SignSpecEnterPasswordModalViewModelTests.swift in Sources */,
6DE48E8D2B1F0B96003094D5 /* AutoMockable+S.generated.swift in Sources */,
6DE48E872B1F0B96003094D5 /* AutoMockable+E.generated.swift in Sources */,
6DE48E8B2B1F0B96003094D5 /* AutoMockable+I.generated.swift in Sources */,
Expand Down Expand Up @@ -2739,6 +2742,7 @@
6DE48E952B1F0B96003094D5 /* AutoMockable+Q.generated.swift in Sources */,
6DE48E802B1F0B96003094D5 /* AutoMockable+P.generated.swift in Sources */,
6D80EB502B4EAD3E009C544B /* VerifierCertificateViewModelTests.swift in Sources */,
6D2C78B02B56EF55006431E3 /* SettingsBackupModalViewModelTests.swift in Sources */,
6D80EB522B4EB0B8009C544B /* MSufficientCryptoReady+Generate.swift in Sources */,
6DE48E8C2B1F0B96003094D5 /* AutoMockable+R.generated.swift in Sources */,
6D5801E5289937BA006C41D8 /* ConnectivityMonitoringAssemblerTests.swift in Sources */,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,10 @@ struct BackupSelectKeyView: View {
onDismiss: { viewModel.seedPhraseToPresent = .init(keyName: "", seedPhrase: .init(seedPhrase: "")) }
) {
SettingsBackupModal(
isShowingBackupModal: $viewModel.isPresentingBackupModal,
viewModel: viewModel.seedPhraseToPresent
viewModel: .init(
isPresented: $viewModel.isPresentingBackupModal,
viewModel: viewModel.seedPhraseToPresent
)
)
.clearModalBackground()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,38 +13,25 @@ struct SettingsBackupViewModel: Equatable {
}

struct SettingsBackupModal: View {
@State private var animateBackground: Bool = false
@Binding private var isShowingBackupModal: Bool
@StateObject private var snackbar: BottomSnackbarPresentation = .init()
private let viewModel: SettingsBackupViewModel

init(
isShowingBackupModal: Binding<Bool>,
viewModel: SettingsBackupViewModel
) {
_isShowingBackupModal = isShowingBackupModal
self.viewModel = viewModel
}
@StateObject var viewModel: ViewModel

var body: some View {
FullScreenRoundedModal(
backgroundTapAction: {
animateDismissal()
},
animateBackground: $animateBackground,
backgroundTapAction: viewModel.dismissModal,
animateBackground: $viewModel.animateBackground,
ignoredEdges: .bottom,
safeAreaInsetsMode: .full,
content: {
VStack(alignment: .center, spacing: 0) {
// Header with X button
HStack {
VStack(alignment: .leading, spacing: Spacing.extraExtraSmall) {
Text(viewModel.keyName)
Text(viewModel.backupViewModel.keyName)
.foregroundColor(.textAndIconsPrimary)
.font(PrimaryFont.titleS.font)
}
Spacer()
CloseModalButton(action: animateDismissal)
CloseModalButton(action: viewModel.dismissModal)
}
.padding(.leading, Spacing.large)
.padding(.trailing, Spacing.medium)
Expand All @@ -59,16 +46,12 @@ struct SettingsBackupModal: View {
seedPhraseContent()
}
}
.onAppear {
snackbar.viewModel = .init(
title: Localizable.Settings.BackupModal.Label.snackbar.string,
style: .info,
tapToDismiss: false,
countdown: .init(counter: 60, viewModel: .snackbarCountdown, onCompletion: animateDismissal)
)
snackbar.isSnackbarPresented = true
}
.bottomSnackbar(snackbar.viewModel, isPresented: $snackbar.isSnackbarPresented, autodismissCounter: 60)
.onAppear { viewModel.onAppear() }
.bottomSnackbar(
viewModel.snackbar.viewModel,
isPresented: $viewModel.snackbar.isSnackbarPresented,
autodismissCounter: 60
)
}
)
}
Expand All @@ -83,33 +66,67 @@ struct SettingsBackupModal: View {
.font(PrimaryFont.bodyL.font)
Spacer()
}
SeedPhraseView(viewModel: .init(dataModel: viewModel.seedPhrase))
SeedPhraseView(viewModel: .init(dataModel: viewModel.backupViewModel.seedPhrase))
Spacer()
.frame(height: Spacing.backupComponentSpacer)
}
.padding(.horizontal, Spacing.large)
.padding(.vertical, Spacing.medium)
}
}

private func animateDismissal() {
Animations.chainAnimation(
animateBackground.toggle(),
delayedAnimationClosure: { isShowingBackupModal = false }()
)
extension SettingsBackupModal {
final class ViewModel: ObservableObject {
@Published var animateBackground: Bool = false
@Published var snackbar: BottomSnackbarPresentation = .init()
@Binding var isPresented: Bool
let backupViewModel: SettingsBackupViewModel

init(
isPresented: Binding<Bool>,
viewModel: SettingsBackupViewModel
) {
_isPresented = isPresented
backupViewModel = viewModel
}

func onAppear() {
snackbar.viewModel = .init(
title: Localizable.Settings.BackupModal.Label.snackbar.string,
style: .info,
tapToDismiss: false,
countdown: .init(counter: 60, viewModel: .snackbarCountdown, onCompletion: dismissModal)
)
snackbar.isSnackbarPresented = true
}

func dismissModal() {
Animations.chainAnimation(
animateBackground.toggle(),
// swiftformat:disable all
delayedAnimationClosure: self.hide()
)
}

private func hide() {
isPresented = false
}
}
}

#if DEBUG
struct SettingsBackupModal_Previews: PreviewProvider {
static var previews: some View {
VStack {
SettingsBackupModal(
isShowingBackupModal: Binding<Bool>.constant(true),
struct SettingsBackupModal_Previews: PreviewProvider {
static var previews: some View {
VStack {
SettingsBackupModal(
viewModel: .init(
isPresented: Binding<Bool>.constant(true),
viewModel: .stub
)
}
.previewLayout(.sizeThatFits)
.preferredColorScheme(.dark)
)
}
.previewLayout(.sizeThatFits)
.preferredColorScheme(.dark)
}
}
#endif
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
//
// SettingsBackupModalViewModelTests.swift
// PolkadotVaultTests
//
// Created by Krzysztof Rodak on 22/01/2024.
//

import Combine
import Foundation
@testable import PolkadotVault
import SwiftUI
import XCTest

final class SettingsBackupModalViewModelTests: XCTestCase {
private var viewModel: SettingsBackupModal.ViewModel!
private var isPresented: Bool!

override func setUp() {
super.setUp()
isPresented = false
viewModel = SettingsBackupModal.ViewModel(
isPresented: Binding(get: { self.isPresented }, set: { self.isPresented = $0 }),
viewModel: SettingsBackupViewModel(
keyName: "Test Key",
seedPhrase: SeedPhraseViewModel(
seedPhrase: "Test Phrase"
)
)
)
}

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

func testInit_SetsAnimateBackgroundToFalse() {
// Then
XCTAssertFalse(viewModel.animateBackground)
}

func testOnAppear_SetsSnackbar() {
// When
viewModel.onAppear()

// Then
XCTAssertEqual(viewModel.snackbar.viewModel.title, Localizable.Settings.BackupModal.Label.snackbar.string)
XCTAssertTrue(viewModel.snackbar.viewModel.tapToDismiss == false)
XCTAssertTrue(viewModel.snackbar.isSnackbarPresented)
}

func testDismissModal_TogglesAnimateBackground() {
// Given
viewModel.animateBackground = false

// When
viewModel.dismissModal()

// Then
XCTAssertTrue(viewModel.animateBackground)
}

func testDismissModal_HidesModal() {
// Given
let expectation = XCTestExpectation()
viewModel.animateBackground = false
viewModel.dismissModal()

DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
// Then
XCTAssertFalse(self.isPresented)
expectation.fulfill()
}

// When
wait(for: [expectation], timeout: 2)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
//
// SignSpecDetailsViewModelTests.swift
// PolkadotVaultTests
//
// Created by Krzysztof Rodak on 19/01/2024.
//

import Combine
import Foundation
@testable import PolkadotVault
import XCTest

final class SignSpecDetailsViewModelTests: XCTestCase {
private var viewModel: SignSpecDetails.ViewModel!
private var cancelBag: CancelBag!
private var metadataSpecsVersion: String!

override func setUp() {
super.setUp()
metadataSpecsVersion = "version"
viewModel = SignSpecDetails.ViewModel(content: .generate(), type: .metadata(metadataSpecsVersion: ""))
cancelBag = CancelBag()
}

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

func testOnBackTap_SendsDismissRequest() {
// Given
let dismissRequestExpectation = expectation(description: "Dismiss request sent")

viewModel.dismissViewRequest
.sink { _ in
// Then
dismissRequestExpectation.fulfill()
}
.store(in: cancelBag)

// When
viewModel.onBackTap()

wait(for: [dismissRequestExpectation], timeout: 1.0)
}

func testTitle_ForMetadataType() {
// Given
viewModel = SignSpecDetails.ViewModel(content: .generate(), type: .metadata(metadataSpecsVersion: ""))

// Then
XCTAssertEqual(viewModel.title, Localizable.SignSpecsDetails.Label.Title.metadata.string)
}

func testTitle_ForNetworkType() {
// Given
viewModel = SignSpecDetails.ViewModel(content: .generate(), type: .network)

// Then
XCTAssertEqual(viewModel.title, Localizable.SignSpecsDetails.Label.Title.specs.string)
}

func testQRCodeSectionTitle_ForMetadataType() {
// Given
viewModel = SignSpecDetails.ViewModel(content: .generate(), type: .metadata(metadataSpecsVersion: ""))

// Then
XCTAssertEqual(viewModel.qrCodeSectionTitle, Localizable.SignSpecsDetails.Label.ScanQRCode.metadata.string)
}

func testQRCodeSectionTitle_ForNetworkType() {
// Given
viewModel = SignSpecDetails.ViewModel(content: .generate(), type: .network)

// Then
XCTAssertEqual(viewModel.qrCodeSectionTitle, Localizable.SignSpecsDetails.Label.ScanQRCode.specs.string)
}
}
Loading

0 comments on commit c39dd48

Please sign in to comment.