[PM-14425] [BEEEP] Add test plans (#1106)

Co-authored-by: Katherine Bertelsen <kbertelsen@bitwarden.com>
This commit is contained in:
Federico Maccaroni 2025-10-10 14:03:19 -03:00 committed by GitHub
parent 11128e13f6
commit 07176cd3c0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
228 changed files with 8022 additions and 4277 deletions

View File

@ -5,8 +5,6 @@ import XCTest
// MARK: - OTPAuthModelTests
class OTPAuthModelTests: BitwardenTestCase {
// MARK: Tests
// MARK: Init Success
/// `init` parses an account if there is no issuer

View File

@ -1,6 +1,6 @@
// swiftlint:disable:this file_name
import SnapshotTesting
import SwiftUI
import ViewInspector
import XCTest
@testable import AuthenticatorShared

View File

@ -0,0 +1,57 @@
// swiftlint:disable:this file_name
import BitwardenKit
import BitwardenResources
import SnapshotTesting
import XCTest
@testable import AuthenticatorShared
// MARK: - DebugMenuViewTests
class DebugMenuViewTests: BitwardenTestCase {
// MARK: Properties
var processor: MockProcessor<DebugMenuState, DebugMenuAction, DebugMenuEffect>!
var subject: DebugMenuView!
// MARK: Setup & Teardown
override func setUp() {
super.setUp()
processor = MockProcessor(
state: DebugMenuState(
featureFlags: [
.init(
feature: .testFeatureFlag,
isEnabled: false,
),
],
),
)
let store = Store(processor: processor)
subject = DebugMenuView(store: store)
}
override func tearDown() {
super.tearDown()
processor = nil
subject = nil
}
// MARK: Tests
/// Check the snapshot when feature flags are enabled and disabled.
@MainActor
func disabletest_snapshot_debugMenuWithFeatureFlags() {
processor.state.featureFlags = [
.init(
feature: .testFeatureFlag,
isEnabled: true,
),
]
assertSnapshot(of: subject, as: .defaultPortrait)
}
}

View File

@ -1,6 +1,7 @@
// swiftlint:disable:this file_name
import BitwardenKit
import BitwardenResources
import SnapshotTesting
import ViewInspector
import XCTest
@testable import AuthenticatorShared
@ -64,21 +65,9 @@ class DebugMenuViewTests: BitwardenTestCase {
/// Test that the refresh button sends the correct effect.
@MainActor
func disabletest_snapshot_refreshFeatureFlags_tapped() async throws {
func disabletest_refreshFeatureFlags_tapped() async throws {
let button = try subject.inspect().find(asyncButtonWithAccessibilityLabel: "RefreshFeatureFlagsButton")
try await button.tap()
XCTAssertEqual(processor.effects.last, .refreshFeatureFlags)
}
/// Check the snapshot when feature flags are enabled and disabled.
@MainActor
func disabletest_snapshot_debugMenuWithFeatureFlags() {
processor.state.featureFlags = [
.init(
feature: .testFeatureFlag,
isEnabled: true,
),
]
assertSnapshot(of: subject, as: .defaultPortrait)
}
}

View File

@ -1,39 +0,0 @@
import XCTest
@testable import AuthenticatorShared
// MARK: - AppCoordinatorTests
class AppCoordinatorTests: BitwardenTestCase {
// MARK: Properties
var module: MockAppModule!
var rootNavigator: MockRootNavigator!
var services: Services!
var subject: AppCoordinator!
// MARK: Setup & Teardown
override func setUp() {
super.setUp()
module = MockAppModule()
rootNavigator = MockRootNavigator()
services = ServiceContainer.withMocks()
subject = AppCoordinator(
appContext: .mainApp,
module: module,
rootNavigator: rootNavigator,
services: services,
)
}
override func tearDown() {
super.tearDown()
module = nil
rootNavigator = nil
services = nil
subject = nil
}
}

View File

@ -1,27 +0,0 @@
import SwiftUI
import XCTest
@testable import AuthenticatorShared
class AppModuleTests: BitwardenTestCase {
// MARK: Properties
var rootViewController: RootViewController!
var subject: DefaultAppModule!
// MARK: Setup & Teardown
override func setUp() {
super.setUp()
rootViewController = RootViewController()
subject = DefaultAppModule(services: .withMocks())
}
override func tearDown() {
super.tearDown()
rootViewController = nil
subject = nil
}
}

View File

@ -0,0 +1,38 @@
// swiftlint:disable:this file_name
import BitwardenResources
import SnapshotTesting
import XCTest
@testable import AuthenticatorShared
class ExportItemsViewTests: BitwardenTestCase {
// MARK: Properties
var processor: MockProcessor<ExportItemsState, ExportItemsAction, ExportItemsEffect>!
var subject: ExportItemsView!
// MARK: Setup & Teardown
override func setUp() {
super.setUp()
processor = MockProcessor(state: ExportItemsState())
let store = Store(processor: processor)
subject = ExportItemsView(store: store)
}
override func tearDown() {
super.tearDown()
processor = nil
subject = nil
}
// MARK: Snapshots
/// The empty view renders correctly.
func disabletest_snapshot_empty() {
assertSnapshots(of: subject, as: [.defaultPortrait, .defaultPortraitDark, .defaultPortraitAX5])
}
}

View File

@ -1,5 +1,5 @@
// swiftlint:disable:this file_name
import BitwardenResources
import SnapshotTesting
import XCTest
@testable import AuthenticatorShared
@ -55,11 +55,4 @@ class ExportItemsViewTests: BitwardenTestCase {
try menuField.select(newValue: ExportFormatType.csv)
XCTAssertEqual(processor.dispatchedActions.last, .fileFormatTypeChanged(.csv))
}
// MARK: Snapshots
/// The empty view renders correctly.
func disabletest_snapshot_empty() {
assertSnapshots(of: subject, as: [.defaultPortrait, .defaultPortraitDark, .defaultPortraitAX5])
}
}

View File

@ -0,0 +1,38 @@
// swiftlint:disable:this file_name
import BitwardenResources
import SnapshotTesting
import XCTest
@testable import AuthenticatorShared
class ImportItemsViewTests: BitwardenTestCase {
// MARK: Properties
var processor: MockProcessor<ImportItemsState, ImportItemsAction, ImportItemsEffect>!
var subject: ImportItemsView!
// MARK: Setup & Teardown
override func setUp() {
super.setUp()
processor = MockProcessor(state: ImportItemsState())
let store = Store(processor: processor)
subject = ImportItemsView(store: store)
}
override func tearDown() {
super.tearDown()
processor = nil
subject = nil
}
// MARK: Snapshots
/// The empty view renders correctly.
func disabletest_snapshot_empty() {
assertSnapshots(of: subject, as: [.defaultPortrait, .defaultPortraitDark, .defaultPortraitAX5])
}
}

View File

@ -1,5 +1,5 @@
// swiftlint:disable:this file_name
import BitwardenResources
import SnapshotTesting
import XCTest
@testable import AuthenticatorShared
@ -55,11 +55,4 @@ class ImportItemsViewTests: BitwardenTestCase {
try menuField.select(newValue: ImportFormatType.bitwardenJson)
XCTAssertEqual(processor.dispatchedActions.last, .fileFormatTypeChanged(.bitwardenJson))
}
// MARK: Snapshots
/// The empty view renders correctly.
func disabletest_snapshot_empty() {
assertSnapshots(of: subject, as: [.defaultPortrait, .defaultPortraitDark, .defaultPortraitAX5])
}
}

View File

@ -0,0 +1,43 @@
// swiftlint:disable:this file_name
import BitwardenResources
import SnapshotTesting
import XCTest
// MARK: - SelectLanguageViewTests
@testable import AuthenticatorShared
class SelectLanguageViewTests: BitwardenTestCase {
// MARK: Properties
var processor: MockProcessor<SelectLanguageState, SelectLanguageAction, Void>!
var subject: SelectLanguageView!
// MARK: Setup & Teardown
override func setUp() {
super.setUp()
processor = MockProcessor(state: SelectLanguageState())
let store = Store(processor: processor)
subject = SelectLanguageView(store: store)
}
override func tearDown() {
super.tearDown()
processor = nil
subject = nil
}
// MARK: Snapshots
/// Test that the view renders correctly.
func disabletest_viewRender() {
assertSnapshots(
of: subject.navStackWrapped,
as: [.defaultPortrait, .defaultPortraitDark, .tallPortraitAX5()],
)
}
}

View File

@ -1,5 +1,5 @@
// swiftlint:disable:this file_name
import BitwardenResources
import SnapshotTesting
import XCTest
// MARK: - SelectLanguageViewTests
@ -47,14 +47,4 @@ class SelectLanguageViewTests: BitwardenTestCase {
try button.tap()
XCTAssertEqual(processor.dispatchedActions.last, .languageTapped(.default))
}
// MARK: Snapshots
/// Test that the view renders correctly.
func disabletest_viewRender() {
assertSnapshots(
of: subject.navStackWrapped,
as: [.defaultPortrait, .defaultPortraitDark, .tallPortraitAX5()],
)
}
}

View File

@ -0,0 +1,67 @@
// swiftlint:disable:this file_name
import BitwardenKit
import BitwardenResources
import SnapshotTesting
import XCTest
// MARK: - SettingsViewTests
@testable import AuthenticatorShared
class SettingsViewTests: BitwardenTestCase {
// MARK: Properties
let copyrightText = "© Bitwarden Inc. 2015-2024"
let version = "Version: 1.0.0 (1)"
var processor: MockProcessor<SettingsState, SettingsAction, SettingsEffect>!
var subject: SettingsView!
// MARK: Setup & Teardown
override func setUp() {
super.setUp()
processor = MockProcessor(state: SettingsState(copyrightText: copyrightText, version: version))
let store = Store(processor: processor)
subject = SettingsView(store: store)
}
override func tearDown() {
super.tearDown()
processor = nil
subject = nil
}
// MARK: Tests
/// Tests the view renders correctly.
func disabletest_snapshot_viewRender() {
assertSnapshots(
of: subject,
as: [.defaultPortrait, .defaultPortraitDark, .defaultPortraitAX5],
)
}
/// Tests the view renders correctly.
@MainActor
func disabletest_snapshot_viewRenderWithBiometricsAvailable() {
processor.state.biometricUnlockStatus = .available(.faceID, enabled: false, hasValidIntegrity: true)
assertSnapshots(
of: subject,
as: [.defaultPortrait, .defaultPortraitDark, .defaultPortraitAX5],
)
}
/// Tests the view renders correctly with `shouldShowDefaultSaveOption` set to `true`.
@MainActor
func disabletest_snapshot_viewRenderWithDefaultSaveOption() {
processor.state.shouldShowDefaultSaveOption = true
assertSnapshots(
of: subject,
as: [.defaultPortrait, .defaultPortraitDark, .defaultPortraitAX5],
)
}
}

View File

@ -1,6 +1,6 @@
// swiftlint:disable:this file_name
import BitwardenKit
import BitwardenResources
import SnapshotTesting
import XCTest
// MARK: - SettingsViewTests
@ -130,32 +130,4 @@ class SettingsViewTests: BitwardenTestCase {
try button.tap()
XCTAssertEqual(processor.dispatchedActions.last, .versionTapped)
}
/// Tests the view renders correctly.
func disabletest_snapshot_viewRender() {
assertSnapshots(
of: subject,
as: [.defaultPortrait, .defaultPortraitDark, .defaultPortraitAX5],
)
}
/// Tests the view renders correctly.
@MainActor
func disabletest_snapshot_viewRenderWithBiometricsAvailable() {
processor.state.biometricUnlockStatus = .available(.faceID, enabled: false, hasValidIntegrity: true)
assertSnapshots(
of: subject,
as: [.defaultPortrait, .defaultPortraitDark, .defaultPortraitAX5],
)
}
/// Tests the view renders correctly with `shouldShowDefaultSaveOption` set to `true`.
@MainActor
func disabletest_snapshot_viewRenderWithDefaultSaveOption() {
processor.state.shouldShowDefaultSaveOption = true
assertSnapshots(
of: subject,
as: [.defaultPortrait, .defaultPortraitDark, .defaultPortraitAX5],
)
}
}

View File

@ -1,6 +1,6 @@
// swiftlint:disable:this file_name
import SnapshotTesting
import SwiftUI
import ViewInspector
import XCTest
@testable import AuthenticatorShared

View File

@ -1,6 +1,6 @@
// swiftlint:disable:this file_name
import SnapshotTesting
import SwiftUI
import ViewInspector
import XCTest
@testable import AuthenticatorShared

View File

@ -0,0 +1,27 @@
// swiftlint:disable:this file_name
import BitwardenResources
import SnapshotTesting
import XCTest
@testable import AuthenticatorShared
// MARK: - ItemListCardViewTests
class ItemListCardViewTests: BitwardenTestCase {
// MARK: Tests
/// Test a snapshot of the ItemListView previews.
func disabletest_snapshot_ItemListCardView_previews() {
for preview in ItemListCardView_Previews._allPreviews {
let name = preview.displayName ?? "Unknown"
assertSnapshots(
of: preview.content,
as: [
"\(name)-portrait": .defaultPortrait,
"\(name)-portraitDark": .defaultPortraitDark,
"\(name)-portraitAX5": .tallPortraitAX5(heightMultiple: 3),
],
)
}
}
}

View File

@ -1,5 +1,5 @@
// swiftlint:disable:this file_name
import BitwardenResources
import SnapshotTesting
import ViewInspector
import XCTest
@ -10,23 +10,8 @@ import XCTest
class ItemListCardViewTests: BitwardenTestCase {
// MARK: Tests
/// Test a snapshot of the ItemListView previews.
func disabletest_snapshot_ItemListCardView_previews() {
for preview in ItemListCardView_Previews._allPreviews {
let name = preview.displayName ?? "Unknown"
assertSnapshots(
of: preview.content,
as: [
"\(name)-portrait": .defaultPortrait,
"\(name)-portraitDark": .defaultPortraitDark,
"\(name)-portraitAX5": .tallPortraitAX5(heightMultiple: 3),
],
)
}
}
/// Test the actions are properly wired up in the ItemListCardView.
func disabletest_snapshot_ItemListCardView_actions() throws {
func test_ItemListCardView_actions() throws {
let expectationAction = expectation(description: "action Tapped")
let expectationClose = expectation(description: "close Tapped")
let subject = ItemListCardView(

View File

@ -1,8 +1,8 @@
// swiftlint:disable:this file_name
import BitwardenKitMocks
import BitwardenResources
import SnapshotTesting
import SwiftUI
import ViewInspector
import XCTest
@testable import AuthenticatorShared
@ -118,44 +118,4 @@ class ItemListViewTests: BitwardenTestCase {
assertSnapshot(of: NavigationView { subject }, as: .defaultPortrait)
}
/// Test the close taps trigger the associated effect.
@MainActor
func disabletest_snapshot_itemListCardView_close_download() throws {
let state = ItemListState(
itemListCardState: .passwordManagerDownload,
loadingState: .data([ItemListSection.fixture()]),
)
processor = MockProcessor(state: state)
subject = ItemListView(
store: Store(processor: processor),
timeProvider: timeProvider,
)
try subject.inspect().find(buttonWithAccessibilityLabel: Localizations.close).tap()
waitFor(!processor.effects.isEmpty)
XCTAssertEqual(processor.effects.last, .closeCard(.passwordManagerDownload))
}
/// Test the close taps trigger the associated effect.
@MainActor
func disabletest_snapshot_itemListCardView_close_sync() throws {
let state = ItemListState(
itemListCardState: .passwordManagerSync,
loadingState: .data([]),
)
processor = MockProcessor(state: state)
subject = ItemListView(
store: Store(processor: processor),
timeProvider: timeProvider,
)
try subject.inspect().find(buttonWithAccessibilityLabel: Localizations.close).tap()
waitFor(!processor.effects.isEmpty)
XCTAssertEqual(processor.effects.last, .closeCard(.passwordManagerSync))
}
}

View File

@ -0,0 +1,82 @@
// swiftlint:disable:this file_name
import BitwardenKitMocks
import BitwardenResources
import SwiftUI
import ViewInspector
import XCTest
@testable import AuthenticatorShared
// MARK: - ItemListViewTests
class ItemListViewTests: BitwardenTestCase {
// MARK: Properties
var processor: MockProcessor<ItemListState, ItemListAction, ItemListEffect>!
var subject: ItemListView!
var timeProvider: MockTimeProvider!
// MARK: Setup & Teardown
override func setUp() {
super.setUp()
let state = ItemListState()
processor = MockProcessor(state: state)
timeProvider = MockTimeProvider(.mockTime(Date(year: 2023, month: 12, day: 31)))
subject = ItemListView(
store: Store(processor: processor),
timeProvider: timeProvider,
)
}
override func tearDown() {
super.tearDown()
processor = nil
subject = nil
timeProvider = nil
}
// MARK: Tests
/// Test the close taps trigger the associated effect.
@MainActor
func test_itemListCardView_close_download() throws {
let state = ItemListState(
itemListCardState: .passwordManagerDownload,
loadingState: .data([ItemListSection.fixture()]),
)
processor = MockProcessor(state: state)
subject = ItemListView(
store: Store(processor: processor),
timeProvider: timeProvider,
)
try subject.inspect().find(buttonWithAccessibilityLabel: Localizations.close).tap()
waitFor(!processor.effects.isEmpty)
XCTAssertEqual(processor.effects.last, .closeCard(.passwordManagerDownload))
}
/// Test the close taps trigger the associated effect.
@MainActor
func test_itemListCardView_close_sync() throws {
let state = ItemListState(
itemListCardState: .passwordManagerSync,
loadingState: .data([]),
)
processor = MockProcessor(state: state)
subject = ItemListView(
store: Store(processor: processor),
timeProvider: timeProvider,
)
try subject.inspect().find(buttonWithAccessibilityLabel: Localizations.close).tap()
waitFor(!processor.effects.isEmpty)
XCTAssertEqual(processor.effects.last, .closeCard(.passwordManagerSync))
}
}

View File

@ -0,0 +1,210 @@
// swiftlint:disable:this file_name
import AVFoundation
import BitwardenKitMocks
import SwiftUI
import XCTest
@testable import AuthenticatorShared
class AuthenticatorKeyCaptureCoordinatorTests: BitwardenTestCase {
// MARK: Properties
var cameraService: MockCameraService!
var delegate: MockAuthenticatorKeyCaptureDelegate!
var errorReporter: MockErrorReporter!
var stackNavigator: MockStackNavigator!
var subject: AuthenticatorKeyCaptureCoordinator!
// MARK: Setup & Teardown
override func setUp() {
super.setUp()
cameraService = MockCameraService()
delegate = MockAuthenticatorKeyCaptureDelegate()
errorReporter = MockErrorReporter()
stackNavigator = MockStackNavigator()
subject = AuthenticatorKeyCaptureCoordinator(
delegate: delegate,
services: ServiceContainer.withMocks(
cameraService: cameraService,
errorReporter: errorReporter,
),
stackNavigator: stackNavigator,
)
cameraService.cameraAuthorizationStatus = .authorized
cameraService.startResult = .success(AVCaptureSession())
}
override func tearDown() {
super.tearDown()
cameraService = nil
errorReporter = nil
stackNavigator = nil
subject = nil
}
// MARK: Tests
/// `navigate(to:)` with `.scanCode` shows the scan view.
@MainActor
func test_navigateTo_scanCode() throws {
cameraService.deviceHasCamera = true
let task = Task {
await subject.handleEvent(.showScanCode)
}
waitFor(!stackNavigator.actions.isEmpty)
task.cancel()
let action = try XCTUnwrap(stackNavigator.actions.last)
XCTAssertEqual(action.type, .replaced)
let view = action.view as? (any View)
XCTAssertNotNil(try? view?.inspect().find(ScanCodeView.self))
}
/// `navigate(to:)` with `.scanCode` shows the scan view.
@MainActor
func test_navigateAsyncTo_scanCode() throws {
cameraService.deviceHasCamera = true
let task = Task {
await subject.handleEvent(.showScanCode)
}
waitFor(!stackNavigator.actions.isEmpty)
task.cancel()
let action = try XCTUnwrap(stackNavigator.actions.last)
XCTAssertEqual(action.type, .replaced)
let view = action.view as? (any View)
XCTAssertNotNil(try? view?.inspect().find(ScanCodeView.self))
}
/// `navigate(to:)` with `.scanCode` shows the scan view.
@MainActor
func test_navigateAsyncTo_scanCode_cameraSessionError() throws {
cameraService.deviceHasCamera = true
struct TestError: Error, Equatable {}
cameraService.startResult = .failure(TestError())
let task = Task {
await subject.handleEvent(.showScanCode)
}
waitFor(!stackNavigator.actions.isEmpty)
task.cancel()
XCTAssertEqual(errorReporter.errors.last as? TestError, TestError())
let action = try XCTUnwrap(stackNavigator.actions.last)
XCTAssertEqual(action.type, .replaced)
let view = action.view as? (any View)
XCTAssertNotNil(try? view?.inspect().find(ManualEntryView.self))
}
/// `navigate(to:)` with `.scanCode` shows the scan view.
@MainActor
func test_navigateAsyncTo_scanCode_declineAuthorization() throws {
cameraService.deviceHasCamera = true
cameraService.cameraAuthorizationStatus = .denied
let task = Task {
await subject.handleEvent(.showScanCode)
}
waitFor(!stackNavigator.actions.isEmpty)
task.cancel()
let action = try XCTUnwrap(stackNavigator.actions.last)
XCTAssertEqual(action.type, .replaced)
let view = action.view as? (any View)
XCTAssertNotNil(try? view?.inspect().find(ManualEntryView.self))
}
/// `navigate(to:)` with `.scanCode` shows the scan view.
@MainActor
func test_navigateAsyncTo_scanCode_noCamera() throws {
cameraService.deviceHasCamera = false
let task = Task {
await subject.handleEvent(.showScanCode)
}
waitFor(!stackNavigator.actions.isEmpty)
task.cancel()
let action = try XCTUnwrap(stackNavigator.actions.last)
XCTAssertEqual(action.type, .replaced)
let view = action.view as? (any View)
XCTAssertNotNil(try? view?.inspect().find(ManualEntryView.self))
}
}
// MARK: - MockAuthenticatorKeyCaptureDelegate
class MockAuthenticatorKeyCaptureDelegate: AuthenticatorKeyCaptureDelegate {
var capturedCaptureCoordinator: AnyCoordinator<AuthenticatorKeyCaptureRoute, AuthenticatorKeyCaptureEvent>?
var didCancelScanCalled = false
var didCompleteCaptureCalled = false
var didCompleteCaptureKey: String?
var didCompleteCaptureName: String?
var didCompleteAutomaticCaptureCalled = false
var didCompleteAutomaticCaptureKey: String?
var didCompleteManualCaptureCalled = false
var didCompleteManualCaptureKey: String?
var didCompleteManualCaptureName: String?
var didCompleteManualCaptureSendToBitwarden = false
/// A flag to capture a `showCameraScan` call.
var didRequestCamera: Bool = false
/// A flag to capture a `showManualEntry` call.
var didRequestManual: Bool = false
func didCancelScan() {
didCancelScanCalled = true
}
func didCompleteCapture(
_ captureCoordinator: AnyCoordinator<AuthenticatorKeyCaptureRoute, AuthenticatorKeyCaptureEvent>,
key: String,
name: String?,
) {
didCompleteCaptureCalled = true
capturedCaptureCoordinator = captureCoordinator
didCompleteCaptureKey = key
didCompleteCaptureName = name
}
func didCompleteAutomaticCapture(
_ captureCoordinator: AnyCoordinator<AuthenticatorKeyCaptureRoute, AuthenticatorKeyCaptureEvent>,
key: String,
) {
didCompleteAutomaticCaptureCalled = true
capturedCaptureCoordinator = captureCoordinator
didCompleteAutomaticCaptureKey = key
}
func didCompleteManualCapture(
_ captureCoordinator: AnyCoordinator<AuthenticatorKeyCaptureRoute, AuthenticatorKeyCaptureEvent>,
key: String,
name: String,
sendToBitwarden: Bool,
) {
didCompleteManualCaptureCalled = true
capturedCaptureCoordinator = captureCoordinator
didCompleteManualCaptureKey = key
didCompleteManualCaptureName = name
didCompleteManualCaptureSendToBitwarden = sendToBitwarden
}
func showCameraScan(
_ captureCoordinator: AnyCoordinator<AuthenticatorKeyCaptureRoute, AuthenticatorKeyCaptureEvent>,
) {
didRequestCamera = true
capturedCaptureCoordinator = captureCoordinator
}
func showManualEntry(
_ captureCoordinator: AnyCoordinator<AuthenticatorKeyCaptureRoute, AuthenticatorKeyCaptureEvent>,
) {
didRequestManual = true
capturedCaptureCoordinator = captureCoordinator
}
}

View File

@ -155,22 +155,6 @@ class AuthenticatorKeyCaptureCoordinatorTests: BitwardenTestCase {
XCTAssertTrue(stackNavigator.actions.isEmpty)
}
/// `navigate(to:)` with `.scanCode` shows the scan view.
@MainActor
func test_navigateTo_scanCode() throws {
cameraService.deviceHasCamera = true
let task = Task {
await subject.handleEvent(.showScanCode)
}
waitFor(!stackNavigator.actions.isEmpty)
task.cancel()
let action = try XCTUnwrap(stackNavigator.actions.last)
XCTAssertEqual(action.type, .replaced)
let view = action.view as? (any View)
XCTAssertNotNil(try? view?.inspect().find(ScanCodeView.self))
}
/// `navigate(to:)` with `.scanCode` shows the scan view.
@MainActor
func test_navigateTo_scanCode_nonEmptyStack() throws {
@ -200,74 +184,6 @@ class AuthenticatorKeyCaptureCoordinatorTests: BitwardenTestCase {
XCTAssertNil(window.viewWithTag(LoadingOverlayDisplayHelper.overlayViewTag))
}
/// `navigate(to:)` with `.scanCode` shows the scan view.
@MainActor
func test_navigateAsyncTo_scanCode() throws {
cameraService.deviceHasCamera = true
let task = Task {
await subject.handleEvent(.showScanCode)
}
waitFor(!stackNavigator.actions.isEmpty)
task.cancel()
let action = try XCTUnwrap(stackNavigator.actions.last)
XCTAssertEqual(action.type, .replaced)
let view = action.view as? (any View)
XCTAssertNotNil(try? view?.inspect().find(ScanCodeView.self))
}
/// `navigate(to:)` with `.scanCode` shows the scan view.
@MainActor
func test_navigateAsyncTo_scanCode_cameraSessionError() throws {
cameraService.deviceHasCamera = true
struct TestError: Error, Equatable {}
cameraService.startResult = .failure(TestError())
let task = Task {
await subject.handleEvent(.showScanCode)
}
waitFor(!stackNavigator.actions.isEmpty)
task.cancel()
XCTAssertEqual(errorReporter.errors.last as? TestError, TestError())
let action = try XCTUnwrap(stackNavigator.actions.last)
XCTAssertEqual(action.type, .replaced)
let view = action.view as? (any View)
XCTAssertNotNil(try? view?.inspect().find(ManualEntryView.self))
}
/// `navigate(to:)` with `.scanCode` shows the scan view.
@MainActor
func test_navigateAsyncTo_scanCode_declineAuthorization() throws {
cameraService.deviceHasCamera = true
cameraService.cameraAuthorizationStatus = .denied
let task = Task {
await subject.handleEvent(.showScanCode)
}
waitFor(!stackNavigator.actions.isEmpty)
task.cancel()
let action = try XCTUnwrap(stackNavigator.actions.last)
XCTAssertEqual(action.type, .replaced)
let view = action.view as? (any View)
XCTAssertNotNil(try? view?.inspect().find(ManualEntryView.self))
}
/// `navigate(to:)` with `.scanCode` shows the scan view.
@MainActor
func test_navigateAsyncTo_scanCode_noCamera() throws {
cameraService.deviceHasCamera = false
let task = Task {
await subject.handleEvent(.showScanCode)
}
waitFor(!stackNavigator.actions.isEmpty)
task.cancel()
let action = try XCTUnwrap(stackNavigator.actions.last)
XCTAssertEqual(action.type, .replaced)
let view = action.view as? (any View)
XCTAssertNotNil(try? view?.inspect().find(ManualEntryView.self))
}
/// `navigate(to:)` with `.scanCode` shows the scan view.
@MainActor
func test_navigateAsyncTo_scanCode_nonEmptyStack() throws {

View File

@ -0,0 +1,116 @@
// swiftlint:disable:this file_name
import BitwardenResources
import SnapshotTesting
import XCTest
@testable import AuthenticatorShared
// MARK: - ManualEntryViewTests
class ManualEntryViewTests: BitwardenTestCase {
// MARK: Properties
var processor: MockProcessor<ManualEntryState, ManualEntryAction, ManualEntryEffect>!
var subject: ManualEntryView!
// MARK: Setup & Teardown
override func setUp() {
super.setUp()
processor = MockProcessor(state: DefaultEntryState(deviceSupportsCamera: true))
let store = Store(processor: processor)
subject = ManualEntryView(
store: store,
)
}
override func tearDown() {
super.tearDown()
processor = nil
subject = nil
}
// MARK: Snapshots
/// Test a snapshot of the `ManualEntryView` empty state.
func disabletest_snapshot_manualEntryView_empty() {
assertSnapshot(
of: ManualEntryView_Previews.empty,
as: .defaultPortrait,
)
}
/// Test a snapshot of the `ManualEntryView` empty state.
func disabletest_snapshot_manualEntryView_empty_landscape() {
assertSnapshot(
of: ManualEntryView_Previews.empty,
as: .defaultLandscape,
)
}
/// Test a snapshot of the `ManualEntryView` in dark mode.
func disabletest_snapshot_manualEntryView_text_dark() {
assertSnapshot(
of: ManualEntryView_Previews.textAdded,
as: .defaultPortraitDark,
)
}
/// Test a snapshot of the `ManualEntryView` with large text.
func disabletest_snapshot_manualEntryView_text_largeText() {
assertSnapshot(
of: ManualEntryView_Previews.textAdded,
as: .tallPortraitAX5(heightMultiple: 1.75),
)
}
/// Test a snapshot of the `ManualEntryView` in light mode.
func disabletest_snapshot_manualEntryView_text_light() {
assertSnapshot(
of: ManualEntryView_Previews.textAdded,
as: .defaultPortrait,
)
}
/// Test a snapshot of the `ManualEntryView` in dark mode with the
/// password manager sync flag active.
func disabletest_snapshot_manualEntryView_text_dark_syncActive() {
assertSnapshot(
of: ManualEntryView_Previews.syncActiveNoDefault,
as: .defaultPortraitDark,
)
}
/// Test a snapshot of the `ManualEntryView` with large text with the
/// password manager sync flag active.
func disabletest_snapshot_manualEntryView_text_largeText_syncActive() {
assertSnapshot(
of: ManualEntryView_Previews.syncActiveNoDefault,
as: .tallPortraitAX5(heightMultiple: 1.75),
)
}
/// Test a snapshot of the `ManualEntryView` in light mode with the
/// password manager sync flag active.
func disabletest_snapshot_manualEntryView_text_light_syncActive() {
assertSnapshot(
of: ManualEntryView_Previews.syncActiveNoDefault,
as: .defaultPortrait,
)
}
/// Test a snapshot of the `ManualEntryView` previews.
func disabletest_snapshot_manualEntryView_previews() {
for preview in ManualEntryView_Previews._allPreviews {
let name = preview.displayName ?? "Unknown"
assertSnapshots(
of: preview.content,
as: [
"\(name)-portrait": .defaultPortrait,
"\(name)-portraitDark": .defaultPortraitDark,
"\(name)-portraitAX5": .defaultPortraitAX5,
],
)
}
}
}

View File

@ -1,5 +1,5 @@
// swiftlint:disable:this file_name
import BitwardenResources
import SnapshotTesting
import ViewInspector
import XCTest
@ -127,88 +127,4 @@ class ManualEntryViewTests: BitwardenTestCase {
XCTAssertEqual(processor.effects.last, .scanCodePressed)
}
// MARK: Snapshots
/// Test a snapshot of the `ManualEntryView` empty state.
func disabletest_snapshot_manualEntryView_empty() {
assertSnapshot(
of: ManualEntryView_Previews.empty,
as: .defaultPortrait,
)
}
/// Test a snapshot of the `ManualEntryView` empty state.
func disabletest_snapshot_manualEntryView_empty_landscape() {
assertSnapshot(
of: ManualEntryView_Previews.empty,
as: .defaultLandscape,
)
}
/// Test a snapshot of the `ManualEntryView` in dark mode.
func disabletest_snapshot_manualEntryView_text_dark() {
assertSnapshot(
of: ManualEntryView_Previews.textAdded,
as: .defaultPortraitDark,
)
}
/// Test a snapshot of the `ManualEntryView` with large text.
func disabletest_snapshot_manualEntryView_text_largeText() {
assertSnapshot(
of: ManualEntryView_Previews.textAdded,
as: .tallPortraitAX5(heightMultiple: 1.75),
)
}
/// Test a snapshot of the `ManualEntryView` in light mode.
func disabletest_snapshot_manualEntryView_text_light() {
assertSnapshot(
of: ManualEntryView_Previews.textAdded,
as: .defaultPortrait,
)
}
/// Test a snapshot of the `ManualEntryView` in dark mode with the
/// password manager sync flag active.
func disabletest_snapshot_manualEntryView_text_dark_syncActive() {
assertSnapshot(
of: ManualEntryView_Previews.syncActiveNoDefault,
as: .defaultPortraitDark,
)
}
/// Test a snapshot of the `ManualEntryView` with large text with the
/// password manager sync flag active.
func disabletest_snapshot_manualEntryView_text_largeText_syncActive() {
assertSnapshot(
of: ManualEntryView_Previews.syncActiveNoDefault,
as: .tallPortraitAX5(heightMultiple: 1.75),
)
}
/// Test a snapshot of the `ManualEntryView` in light mode with the
/// password manager sync flag active.
func disabletest_snapshot_manualEntryView_text_light_syncActive() {
assertSnapshot(
of: ManualEntryView_Previews.syncActiveNoDefault,
as: .defaultPortrait,
)
}
/// Test a snapshot of the `ManualEntryView` previews.
func disabletest_snapshot_manualEntryView_previews() {
for preview in ManualEntryView_Previews._allPreviews {
let name = preview.displayName ?? "Unknown"
assertSnapshots(
of: preview.content,
as: [
"\(name)-portrait": .defaultPortrait,
"\(name)-portraitDark": .defaultPortraitDark,
"\(name)-portraitAX5": .defaultPortraitAX5,
],
)
}
}
}

View File

@ -1,7 +1,7 @@
// swiftlint:disable:this file_name
import AVFoundation
import BitwardenResources
import SnapshotTesting
import ViewInspector
import XCTest
@testable import AuthenticatorShared
@ -32,16 +32,6 @@ class ScanCodeViewTests: BitwardenTestCase {
subject = nil
}
// MARK: Tests
/// Tapping the cancel button dispatches the `.dismiss` action.
@MainActor
func test_cancelButton_tap() throws {
let button = try subject.inspect().findCancelToolbarButton()
try button.tap()
XCTAssertEqual(processor.dispatchedActions.last, .dismissPressed)
}
// MARK: Snapshots
/// Test a snapshot of the ProfileSwitcherView previews.

View File

@ -0,0 +1,44 @@
// swiftlint:disable:this file_name
import AVFoundation
import BitwardenResources
import ViewInspector
import XCTest
@testable import AuthenticatorShared
// MARK: - ScanCodeViewTests
class ScanCodeViewTests: BitwardenTestCase {
// MARK: Properties
var processor: MockProcessor<ScanCodeState, ScanCodeAction, ScanCodeEffect>!
var subject: ScanCodeView!
// MARK: Setup & Teardown
override func setUp() {
super.setUp()
processor = MockProcessor(state: ScanCodeState(showManualEntry: true))
let store = Store(processor: processor)
subject = ScanCodeView(
cameraSession: .init(),
store: store,
)
}
override func tearDown() {
super.tearDown()
processor = nil
subject = nil
}
// MARK: Tests
/// Tapping the cancel button dispatches the `.dismiss` action.
@MainActor
func test_cancelButton_tap() throws {
let button = try subject.inspect().findCancelToolbarButton()
try button.tap()
XCTAssertEqual(processor.dispatchedActions.last, .dismissPressed)
}
}

View File

@ -1,6 +0,0 @@
import SwiftUI
import XCTest
@testable import BitwardenActionExtension
class ActionViewControllerTests: BitwardenTestCase {}

View File

@ -1,6 +0,0 @@
import SwiftUI
import XCTest
@testable import BitwardenAutoFillExtension
class CredentialProviderViewControllerTests: BitwardenTestCase {}

View File

@ -1,3 +1,4 @@
// swiftlint:disable:this file_name
import BitwardenKit
import BitwardenResources
import SnapshotTesting

View File

@ -1,3 +1,4 @@
// swiftlint:disable:this file_name
import BitwardenKit
import ViewInspector
import XCTest

View File

@ -1,3 +1,4 @@
// swiftlint:disable:this file_name
import BitwardenKit
import SwiftUI
import ViewInspector

View File

@ -1,3 +1,4 @@
// swiftlint:disable:this file_name
import BitwardenKit
import BitwardenResources
import SnapshotTesting

View File

@ -1,3 +1,4 @@
// swiftlint:disable:this file_name
import BitwardenKit
import SwiftUI
import ViewInspector

View File

@ -1,3 +1,4 @@
// swiftlint:disable:this file_name
import BitwardenKit
import SnapshotTesting
import SwiftUI

View File

@ -1,6 +0,0 @@
import SwiftUI
import XCTest
@testable import BitwardenShareExtension
class ShareViewControllerTests: BitwardenTestCase {}

View File

@ -0,0 +1,85 @@
// swiftlint:disable:this file_name
import BitwardenResources
import SnapshotTesting
import SwiftUI
import XCTest
@testable import BitwardenShared
// MARK: - CompleteRegistrationViewTests
class CompleteRegistrationViewTests: BitwardenTestCase {
// MARK: Properties
var processor: MockProcessor<CompleteRegistrationState, CompleteRegistrationAction, CompleteRegistrationEffect>!
var subject: CompleteRegistrationView!
// MARK: Setup & Teardown
override func setUp() {
super.setUp()
processor = MockProcessor(state: CompleteRegistrationState(
emailVerificationToken: "emailVerificationToken",
userEmail: "email@example.com",
))
let store = Store(processor: processor)
subject = CompleteRegistrationView(store: store)
}
override func tearDown() {
super.tearDown()
processor = nil
subject = nil
}
// MARK: Snapshots
/// Tests the view renders correctly.
@MainActor
func disabletest_snapshot_empty_nativeCreateAccountFlow() throws {
assertSnapshots(
of: subject,
as: [
.tallPortrait,
.portraitDark(heightMultiple: 2),
.tallPortraitAX5(),
],
)
}
/// Tests the view renders correctly when text fields are hidden.
@MainActor
func disabletest_snapshot_textFields_hidden_nativeCreateAccountFlow() throws {
processor.state.arePasswordsVisible = false
processor.state.userEmail = "email@example.com"
processor.state.passwordText = "12345"
processor.state.retypePasswordText = "12345"
processor.state.passwordHintText = "wink wink"
processor.state.passwordStrengthScore = 0
assertSnapshot(of: subject, as: .defaultPortrait)
}
/// Tests the view renders correctly when the text fields are all populated.
@MainActor
func disabletest_snapshot_textFields_populated_nativeCreateAccountFlow() throws {
processor.state.arePasswordsVisible = true
processor.state.userEmail = "email@example.com"
processor.state.passwordText = "12345"
processor.state.retypePasswordText = "12345"
processor.state.passwordHintText = "wink wink"
processor.state.passwordStrengthScore = 0
assertSnapshots(of: subject, as: [.defaultPortrait, .defaultPortraitDark, .defaultPortraitAX5])
}
/// Tests the view renders correctly when the toggles are on.
@MainActor
func disabletest_snapshot_toggles_on_nativeCreateAccountFlow() throws {
processor.state.isCheckDataBreachesToggleOn = true
assertSnapshot(of: subject, as: .defaultPortrait)
}
}

View File

@ -1,5 +1,5 @@
// swiftlint:disable:this file_name
import BitwardenResources
import SnapshotTesting
import SwiftUI
import ViewInspector
import XCTest
@ -129,53 +129,4 @@ class CompleteRegistrationViewTests: BitwardenTestCase {
XCTAssertEqual(processor.dispatchedActions.last, .preventAccountLockTapped)
}
// MARK: Snapshots
/// Tests the view renders correctly.
@MainActor
func disabletest_snapshot_empty_nativeCreateAccountFlow() throws {
assertSnapshots(
of: subject,
as: [
.tallPortrait,
.portraitDark(heightMultiple: 2),
.tallPortraitAX5(),
],
)
}
/// Tests the view renders correctly when text fields are hidden.
@MainActor
func disabletest_snapshot_textFields_hidden_nativeCreateAccountFlow() throws {
processor.state.arePasswordsVisible = false
processor.state.userEmail = "email@example.com"
processor.state.passwordText = "12345"
processor.state.retypePasswordText = "12345"
processor.state.passwordHintText = "wink wink"
processor.state.passwordStrengthScore = 0
assertSnapshot(of: subject, as: .defaultPortrait)
}
/// Tests the view renders correctly when the text fields are all populated.
@MainActor
func disabletest_snapshot_textFields_populated_nativeCreateAccountFlow() throws {
processor.state.arePasswordsVisible = true
processor.state.userEmail = "email@example.com"
processor.state.passwordText = "12345"
processor.state.retypePasswordText = "12345"
processor.state.passwordHintText = "wink wink"
processor.state.passwordStrengthScore = 0
assertSnapshots(of: subject, as: [.defaultPortrait, .defaultPortraitDark, .defaultPortraitAX5])
}
/// Tests the view renders correctly when the toggles are on.
@MainActor
func disabletest_snapshot_toggles_on_nativeCreateAccountFlow() throws {
processor.state.isCheckDataBreachesToggleOn = true
assertSnapshot(of: subject, as: .defaultPortrait)
}
}

View File

@ -0,0 +1,38 @@
// swiftlint:disable:this file_name
import BitwardenResources
import SnapshotTesting
import SwiftUI
import XCTest
@testable import BitwardenShared
// MARK: - ExpiredLinkViewTests
class ExpiredLinkViewTests: BitwardenTestCase {
// MARK: Properties
var processor: MockProcessor<ExpiredLinkState, ExpiredLinkAction, Void>!
var subject: ExpiredLinkView!
// MARK: Setup & Teardown
override func setUp() {
super.setUp()
processor = MockProcessor(state: ExpiredLinkState())
subject = ExpiredLinkView(store: Store(processor: processor))
}
override func tearDown() {
super.tearDown()
processor = nil
subject = nil
}
// MARK: Tests
/// Tests the view renders correctly.
@MainActor
func disabletest_snapshot_toggles_on() throws {
assertSnapshots(of: subject, as: [.defaultPortrait, .defaultPortraitDark, .defaultPortraitAX5])
}
}

View File

@ -1,5 +1,5 @@
// swiftlint:disable:this file_name
import BitwardenResources
import SnapshotTesting
import SwiftUI
import ViewInspector
import XCTest
@ -53,10 +53,4 @@ class ExpiredLinkViewTests: BitwardenTestCase {
try button.tap()
XCTAssertEqual(processor.dispatchedActions.last, .logInTapped)
}
/// Tests the view renders correctly.
@MainActor
func disabletest_snapshot_toggles_on() throws {
assertSnapshots(of: subject, as: [.defaultPortrait, .defaultPortraitDark, .defaultPortraitAX5])
}
}

View File

@ -0,0 +1,50 @@
// swiftlint:disable:this file_name
import BitwardenResources
import SnapshotTesting
import XCTest
@testable import BitwardenShared
class MasterPasswordGeneratorViewTests: BitwardenTestCase {
// MARK: Properties
var processor: MockProcessor<
MasterPasswordGeneratorState,
MasterPasswordGeneratorAction,
MasterPasswordGeneratorEffect,
>!
var subject: MasterPasswordGeneratorView!
// MARK: Setup & Teardown
override func setUp() {
super.setUp()
let state = MasterPasswordGeneratorState(generatedPassword: "Imma-Little-Teapot2")
processor = MockProcessor(state: state)
subject = MasterPasswordGeneratorView(store: Store(processor: processor))
}
override func tearDown() {
super.tearDown()
processor = nil
subject = nil
}
// MARK: Snapshots
/// The master password generator view renders correctly.
@MainActor
func disabletest_snapshot_masterPasswordGenerator() {
assertSnapshots(
of: subject.navStackWrapped,
as: [
.defaultPortrait,
.defaultPortraitDark,
.tallPortraitAX5(heightMultiple: 2),
.defaultLandscape,
],
)
}
}

View File

@ -1,5 +1,5 @@
// swiftlint:disable:this file_name
import BitwardenResources
import SnapshotTesting
import ViewInspector
import XCTest
@ -63,20 +63,4 @@ class MasterPasswordGeneratorViewTests: BitwardenTestCase {
try await button.tap()
XCTAssertEqual(processor.effects.last, .save)
}
// MARK: Snapshots
/// The master password generator view renders correctly.
@MainActor
func disabletest_snapshot_masterPasswordGenerator() {
assertSnapshots(
of: subject.navStackWrapped,
as: [
.defaultPortrait,
.defaultPortraitDark,
.tallPortraitAX5(heightMultiple: 2),
.defaultLandscape,
],
)
}
}

View File

@ -0,0 +1,46 @@
// swiftlint:disable:this file_name
import BitwardenResources
import SnapshotTesting
import XCTest
@testable import BitwardenShared
class MasterPasswordGuidanceViewTests: BitwardenTestCase {
// MARK: Properties
var processor: MockProcessor<Void, MasterPasswordGuidanceAction, Void>!
var subject: MasterPasswordGuidanceView!
// MARK: Setup & Teardown
override func setUp() {
super.setUp()
processor = MockProcessor(state: ())
subject = MasterPasswordGuidanceView(store: Store(processor: processor))
}
override func tearDown() {
super.tearDown()
processor = nil
subject = nil
}
// MARK: Snapshots
/// The master password guidance view renders correctly.
@MainActor
func disabletest_snapshot_masterPasswordGuidance() {
assertSnapshots(
of: subject.navStackWrapped,
as: [
.defaultPortrait,
.defaultPortraitDark,
.tallPortraitAX5(heightMultiple: 2),
.defaultLandscape,
],
)
}
}

View File

@ -1,5 +1,5 @@
// swiftlint:disable:this file_name
import BitwardenResources
import SnapshotTesting
import ViewInspector
import XCTest
@ -45,20 +45,4 @@ class MasterPasswordGuidanceViewTests: BitwardenTestCase {
try await button.tap()
XCTAssertEqual(processor.dispatchedActions.last, .generatePasswordPressed)
}
// MARK: Snapshots
/// The master password guidance view renders correctly.
@MainActor
func disabletest_snapshot_masterPasswordGuidance() {
assertSnapshots(
of: subject.navStackWrapped,
as: [
.defaultPortrait,
.defaultPortraitDark,
.tallPortraitAX5(heightMultiple: 2),
.defaultLandscape,
],
)
}
}

View File

@ -1,6 +1,6 @@
// swiftlint:disable:this file_name
import BitwardenResources
import SnapshotTesting
import ViewInspector
import XCTest
@testable import BitwardenShared
@ -27,16 +27,6 @@ class PreventAccountLockViewTests: BitwardenTestCase {
subject = nil
}
// MARK: Tests
/// Tapping the cancel button dispatches the `.dismiss` action.
@MainActor
func test_cancelButton_tap() throws {
let button = try subject.inspect().findCloseToolbarButton()
try button.tap()
XCTAssertEqual(processor.dispatchedActions.last, .dismiss)
}
// MARK: Snapshots
/// The prevent account lock view renders correctly.

View File

@ -0,0 +1,39 @@
// swiftlint:disable:this file_name
import BitwardenResources
import ViewInspector
import XCTest
@testable import BitwardenShared
class PreventAccountLockViewTests: BitwardenTestCase {
// MARK: Properties
var processor: MockProcessor<Void, PreventAccountLockAction, Void>!
var subject: PreventAccountLockView!
// MARK: Setup & Teardown
override func setUp() {
super.setUp()
processor = MockProcessor(state: ())
subject = PreventAccountLockView(store: Store(processor: processor))
}
override func tearDown() {
super.tearDown()
processor = nil
subject = nil
}
// MARK: Tests
/// Tapping the cancel button dispatches the `.dismiss` action.
@MainActor
func test_cancelButton_tap() throws {
let button = try subject.inspect().findCloseToolbarButton()
try button.tap()
XCTAssertEqual(processor.dispatchedActions.last, .dismiss)
}
}

View File

@ -1,6 +1,6 @@
// swiftlint:disable:this file_name
import BitwardenResources
import SnapshotTesting
import ViewInspector
import XCTest
@testable import BitwardenShared
@ -28,24 +28,6 @@ class IntroCarouselViewTests: BitwardenTestCase {
subject = nil
}
// MARK: Tests
/// Tapping the create account button performs the create account effect.
@MainActor
func test_createAccount_tap() async throws {
let button = try subject.inspect().find(asyncButton: Localizations.createAccount)
try await button.tap()
XCTAssertEqual(processor.effects.last, .createAccount)
}
/// Tapping the log in button dispatches the login action.
@MainActor
func test_login_tap() throws {
let button = try subject.inspect().find(button: Localizations.logIn)
try button.tap()
XCTAssertEqual(processor.dispatchedActions.last, .logIn)
}
// MARK: Snapshots
/// The intro carousel page 1 renders correctly.

View File

@ -0,0 +1,48 @@
// swiftlint:disable:this file_name
import BitwardenResources
import ViewInspector
import XCTest
@testable import BitwardenShared
class IntroCarouselViewTests: BitwardenTestCase {
// MARK: Properties
var processor: MockProcessor<IntroCarouselState, IntroCarouselAction, IntroCarouselEffect>!
var subject: IntroCarouselView!
// MARK: Setup & Teardown
override func setUp() {
super.setUp()
processor = MockProcessor(state: IntroCarouselState())
subject = IntroCarouselView(store: Store(processor: processor))
}
override func tearDown() {
super.tearDown()
processor = nil
subject = nil
}
// MARK: Tests
/// Tapping the create account button performs the create account effect.
@MainActor
func test_createAccount_tap() async throws {
let button = try subject.inspect().find(asyncButton: Localizations.createAccount)
try await button.tap()
XCTAssertEqual(processor.effects.last, .createAccount)
}
/// Tapping the log in button dispatches the login action.
@MainActor
func test_login_tap() throws {
let button = try subject.inspect().find(button: Localizations.logIn)
try button.tap()
XCTAssertEqual(processor.dispatchedActions.last, .logIn)
}
}

View File

@ -0,0 +1,90 @@
// swiftlint:disable:this file_name
import BitwardenKit
import BitwardenResources
import SnapshotTesting
import SwiftUI
import XCTest
@testable import BitwardenShared
// MARK: - LandingViewTests
class LandingViewTests: BitwardenTestCase {
// MARK: Properties
var processor: MockProcessor<LandingState, LandingAction, LandingEffect>!
var subject: LandingView!
// MARK: Setup & Teardown
override func setUp() {
super.setUp()
processor = MockProcessor(state: LandingState())
let store = Store(processor: processor)
subject = LandingView(store: store)
}
override func tearDown() {
super.tearDown()
processor = nil
subject = nil
}
// MARK: Snapshots
/// Check the snapshot for the empty state.
@MainActor
func disabletest_snapshot_empty() {
assertSnapshots(of: subject, as: [.defaultPortrait, .defaultPortraitDark, .defaultPortraitAX5])
}
/// Check the snapshot when the email text field has a value.
@MainActor
func disabletest_snapshot_email_value() {
processor.state.email = "email@example.com"
assertSnapshots(of: subject, as: [.defaultPortrait, .defaultPortraitDark, .defaultPortraitAX5])
}
/// Check the snapshot when the remember me toggle is on.
@MainActor
func disabletest_snapshot_isRememberMeOn_true() {
processor.state.isRememberMeOn = true
assertSnapshots(of: subject, as: [.defaultPortrait, .defaultPortraitDark, .defaultPortraitAX5])
}
/// Check the snapshot for the profiles visible
@MainActor
func disabletest_snapshot_profilesVisible() {
let account = ProfileSwitcherItem.fixture(
email: "extra.warden@bitwarden.com",
userInitials: "EW",
)
processor.state.profileSwitcherState = ProfileSwitcherState(
accounts: [
account,
],
activeAccountId: account.userId,
allowLockAndLogout: true,
isVisible: true,
)
assertSnapshots(of: subject, as: [.defaultPortrait, .defaultPortraitDark, .defaultPortraitAX5])
}
/// Check the snapshot for the profiles closed
@MainActor
func disabletest_snapshot_profilesClosed() {
let account = ProfileSwitcherItem.fixture(
email: "extra.warden@bitwarden.com",
userInitials: "EW",
)
processor.state.profileSwitcherState = ProfileSwitcherState(
accounts: [
account,
],
activeAccountId: account.userId,
allowLockAndLogout: true,
isVisible: false,
)
assertSnapshots(of: subject, as: [.defaultPortrait, .defaultPortraitDark, .defaultPortraitAX5])
}
}

View File

@ -1,6 +1,6 @@
// swiftlint:disable:this file_name
import BitwardenKit
import BitwardenResources
import SnapshotTesting
import SwiftUI
import ViewInspector
import XCTest
@ -102,62 +102,4 @@ class LandingViewTests: BitwardenTestCase {
try toggle.tap()
XCTAssertEqual(processor.dispatchedActions.last, .rememberMeChanged(true))
}
// MARK: Snapshots
/// Check the snapshot for the empty state.
@MainActor
func disabletest_snapshot_empty() {
assertSnapshots(of: subject, as: [.defaultPortrait, .defaultPortraitDark, .defaultPortraitAX5])
}
/// Check the snapshot when the email text field has a value.
@MainActor
func disabletest_snapshot_email_value() {
processor.state.email = "email@example.com"
assertSnapshots(of: subject, as: [.defaultPortrait, .defaultPortraitDark, .defaultPortraitAX5])
}
/// Check the snapshot when the remember me toggle is on.
@MainActor
func disabletest_snapshot_isRememberMeOn_true() {
processor.state.isRememberMeOn = true
assertSnapshots(of: subject, as: [.defaultPortrait, .defaultPortraitDark, .defaultPortraitAX5])
}
/// Check the snapshot for the profiles visible
@MainActor
func disabletest_snapshot_profilesVisible() {
let account = ProfileSwitcherItem.fixture(
email: "extra.warden@bitwarden.com",
userInitials: "EW",
)
processor.state.profileSwitcherState = ProfileSwitcherState(
accounts: [
account,
],
activeAccountId: account.userId,
allowLockAndLogout: true,
isVisible: true,
)
assertSnapshots(of: subject, as: [.defaultPortrait, .defaultPortraitDark, .defaultPortraitAX5])
}
/// Check the snapshot for the profiles closed
@MainActor
func disabletest_snapshot_profilesClosed() {
let account = ProfileSwitcherItem.fixture(
email: "extra.warden@bitwarden.com",
userInitials: "EW",
)
processor.state.profileSwitcherState = ProfileSwitcherState(
accounts: [
account,
],
activeAccountId: account.userId,
allowLockAndLogout: true,
isVisible: false,
)
assertSnapshots(of: subject, as: [.defaultPortrait, .defaultPortraitDark, .defaultPortraitAX5])
}
}

View File

@ -0,0 +1,36 @@
// swiftlint:disable:this file_name
import BitwardenResources
import SnapshotTesting
import XCTest
@testable import BitwardenShared
class SelfHostedViewTests: BitwardenTestCase {
// MARK: Properties
var processor: MockProcessor<SelfHostedState, SelfHostedAction, SelfHostedEffect>!
var subject: SelfHostedView!
// MARK: Setup & Teardown
override func setUp() {
super.setUp()
processor = MockProcessor(state: SelfHostedState())
subject = SelfHostedView(store: Store(processor: processor))
}
override func tearDown() {
super.tearDown()
subject = nil
}
// MARK: Snapshots
/// Tests that the view renders correctly.
func disabletest_snapshot_viewRender() {
assertSnapshot(of: subject.navStackWrapped, as: .defaultPortrait)
}
}

View File

@ -1,5 +1,5 @@
// swiftlint:disable:this file_name
import BitwardenResources
import SnapshotTesting
import XCTest
@testable import BitwardenShared
@ -48,11 +48,4 @@ class SelfHostedViewTests: BitwardenTestCase {
try await button.tap()
XCTAssertEqual(processor.effects.last, .saveEnvironment)
}
// MARK: Snapshots
/// Tests that the view renders correctly.
func disabletest_snapshot_viewRender() {
assertSnapshot(of: subject.navStackWrapped, as: .defaultPortrait)
}
}

View File

@ -0,0 +1,52 @@
// swiftlint:disable:this file_name
import BitwardenResources
import SnapshotTesting
import XCTest
@testable import BitwardenShared
class LoginDecryptionOptionsViewTests: BitwardenTestCase {
// MARK: Properties
var processor: MockProcessor<
LoginDecryptionOptionsState,
LoginDecryptionOptionsAction,
LoginDecryptionOptionsEffect,
>!
var subject: LoginDecryptionOptionsView!
// MARK: Setup & Teardown
override func setUp() {
super.setUp()
processor = MockProcessor(
state: LoginDecryptionOptionsState(
shouldShowApproveMasterPasswordButton: true,
shouldShowApproveWithOtherDeviceButton: true,
shouldShowContinueButton: true,
email: "example@bitwarden.com",
isRememberDeviceToggleOn: true,
orgIdentifier: "Bitwarden",
shouldShowAdminApprovalButton: true,
),
)
let store = Store(processor: processor)
subject = LoginDecryptionOptionsView(store: store)
}
override func tearDown() {
super.tearDown()
processor = nil
subject = nil
}
// MARK: Snapshots
/// The default view renders correctly.
func disabletest_snapshot_default() {
assertSnapshots(of: subject, as: [.defaultPortrait, .defaultPortraitDark, .defaultPortraitAX5])
}
}

View File

@ -1,5 +1,5 @@
// swiftlint:disable:this file_name
import BitwardenResources
import SnapshotTesting
import ViewInspector
import XCTest
@ -95,11 +95,4 @@ class LoginDecryptionOptionsViewTests: BitwardenTestCase {
try toggle.tap()
XCTAssertEqual(processor.dispatchedActions.last, .toggleRememberDevice(true))
}
// MARK: Snapshots
/// The default view renders correctly.
func disabletest_snapshot_default() {
assertSnapshots(of: subject, as: [.defaultPortrait, .defaultPortraitDark, .defaultPortraitAX5])
}
}

View File

@ -0,0 +1,73 @@
// swiftlint:disable:this file_name
import BitwardenResources
import SnapshotTesting
import SwiftUI
import XCTest
@testable import BitwardenShared
// MARK: - LoginViewTests
class LoginViewTests: BitwardenTestCase {
// MARK: Properties
var processor: MockProcessor<LoginState, LoginAction, LoginEffect>!
var subject: LoginView!
// MARK: Setup & Teardown
override func setUp() {
super.setUp()
processor = MockProcessor(state: LoginState())
let store = Store(processor: processor)
subject = LoginView(store: store)
}
override func tearDown() {
super.tearDown()
processor = nil
subject = nil
}
// MARK: Snapshots
@MainActor
func disabletest_snapshot_empty() {
processor.state.username = "user@bitwarden.com"
processor.state.serverURLString = "bitwarden.com"
assertSnapshot(of: subject, as: .defaultPortrait)
}
@MainActor
func disabletest_snapshot_passwordHidden() {
processor.state.username = "user@bitwarden.com"
processor.state.masterPassword = "Password"
processor.state.serverURLString = "bitwarden.com"
processor.state.isMasterPasswordRevealed = false
assertSnapshot(of: subject, as: .defaultPortrait)
}
@MainActor
func disabletest_snapshot_passwordRevealed() {
processor.state.username = "user@bitwarden.com"
processor.state.masterPassword = "Password"
processor.state.serverURLString = "bitwarden.com"
processor.state.isMasterPasswordRevealed = true
assertSnapshot(of: subject, as: .defaultPortrait)
}
@MainActor
func disabletest_snapshot_selfHosted() {
processor.state.username = "user@bitwarden.com"
processor.state.serverURLString = "selfhostedserver.com"
assertSnapshot(of: subject, as: .defaultPortrait)
}
@MainActor
func disabletest_snapshot_withDevice() {
processor.state.username = "user@bitwarden.com"
processor.state.isLoginWithDeviceVisible = true
processor.state.serverURLString = "bitwarden.com"
assertSnapshot(of: subject, as: .defaultPortrait)
}
}

View File

@ -1,5 +1,5 @@
// swiftlint:disable:this file_name
import BitwardenResources
import SnapshotTesting
import SwiftUI
import ViewInspector
import XCTest
@ -121,46 +121,4 @@ class LoginViewTests: BitwardenTestCase {
try secureField.setInput("text")
XCTAssertEqual(processor.dispatchedActions.last, .masterPasswordChanged("text"))
}
// MARK: Snapshots
@MainActor
func disabletest_snapshot_empty() {
processor.state.username = "user@bitwarden.com"
processor.state.serverURLString = "bitwarden.com"
assertSnapshot(of: subject, as: .defaultPortrait)
}
@MainActor
func disabletest_snapshot_passwordHidden() {
processor.state.username = "user@bitwarden.com"
processor.state.masterPassword = "Password"
processor.state.serverURLString = "bitwarden.com"
processor.state.isMasterPasswordRevealed = false
assertSnapshot(of: subject, as: .defaultPortrait)
}
@MainActor
func disabletest_snapshot_passwordRevealed() {
processor.state.username = "user@bitwarden.com"
processor.state.masterPassword = "Password"
processor.state.serverURLString = "bitwarden.com"
processor.state.isMasterPasswordRevealed = true
assertSnapshot(of: subject, as: .defaultPortrait)
}
@MainActor
func disabletest_snapshot_selfHosted() {
processor.state.username = "user@bitwarden.com"
processor.state.serverURLString = "selfhostedserver.com"
assertSnapshot(of: subject, as: .defaultPortrait)
}
@MainActor
func disabletest_snapshot_withDevice() {
processor.state.username = "user@bitwarden.com"
processor.state.isLoginWithDeviceVisible = true
processor.state.serverURLString = "bitwarden.com"
assertSnapshot(of: subject, as: .defaultPortrait)
}
}

View File

@ -0,0 +1,42 @@
// swiftlint:disable:this file_name
import BitwardenResources
import SnapshotTesting
import XCTest
@testable import BitwardenShared
class LoginWithDeviceViewTests: BitwardenTestCase {
// MARK: Properties
var processor: MockProcessor<LoginWithDeviceState, LoginWithDeviceAction, LoginWithDeviceEffect>!
var subject: LoginWithDeviceView!
// MARK: Setup & Teardown
override func setUp() {
super.setUp()
processor = MockProcessor(
state: LoginWithDeviceState(
fingerprintPhrase: "some-weird-long-text-thing-as-a-placeholder",
),
)
let store = Store(processor: processor)
subject = LoginWithDeviceView(store: store)
}
override func tearDown() {
super.tearDown()
processor = nil
subject = nil
}
// MARK: Snapshots
/// The default view renders correctly.
func disabletest_snapshot_default() {
assertSnapshots(of: subject, as: [.defaultPortrait, .defaultPortraitDark, .defaultPortraitAX5])
}
}

View File

@ -1,5 +1,5 @@
// swiftlint:disable:this file_name
import BitwardenResources
import SnapshotTesting
import XCTest
@testable import BitwardenShared
@ -57,11 +57,4 @@ class LoginWithDeviceViewTests: BitwardenTestCase {
try button.tap()
XCTAssertEqual(processor.dispatchedActions.last, .dismiss)
}
// MARK: Snapshots
/// The default view renders correctly.
func disabletest_snapshot_default() {
assertSnapshots(of: subject, as: [.defaultPortrait, .defaultPortraitDark, .defaultPortraitAX5])
}
}

View File

@ -0,0 +1,51 @@
// swiftlint:disable:this file_name
import BitwardenResources
import SnapshotTesting
import XCTest
@testable import BitwardenShared
class SingleSignOnViewTests: BitwardenTestCase {
// MARK: Properties
var processor: MockProcessor<SingleSignOnState, SingleSignOnAction, SingleSignOnEffect>!
var subject: SingleSignOnView!
// MARK: Setup & Teardown
override func setUp() {
super.setUp()
processor = MockProcessor(state: SingleSignOnState())
let store = Store(processor: processor)
subject = SingleSignOnView(store: store)
}
override func tearDown() {
super.tearDown()
processor = nil
subject = nil
}
// MARK: Snapshots
/// Tests the view renders correctly when the text field is empty.
func disabletest_snapshot_empty() {
assertSnapshots(
of: subject.navStackWrapped,
as: [.defaultPortrait, .defaultPortraitDark, .defaultPortraitAX5],
)
}
/// Tests the view renders correctly when the text field is populated.
@MainActor
func disabletest_snapshot_populated() {
processor.state.identifierText = "Insert cool identifier here"
assertSnapshots(
of: subject.navStackWrapped,
as: [.defaultPortrait, .defaultPortraitDark, .defaultPortraitAX5],
)
}
}

View File

@ -1,5 +1,5 @@
// swiftlint:disable:this file_name
import BitwardenResources
import SnapshotTesting
import XCTest
@testable import BitwardenShared
@ -54,24 +54,4 @@ class SingleSignOnViewTests: BitwardenTestCase {
XCTAssertEqual(processor.effects.last, .loginTapped)
}
// MARK: Snapshots
/// Tests the view renders correctly when the text field is empty.
func disabletest_snapshot_empty() {
assertSnapshots(
of: subject.navStackWrapped,
as: [.defaultPortrait, .defaultPortraitDark, .defaultPortraitAX5],
)
}
/// Tests the view renders correctly when the text field is populated.
@MainActor
func disabletest_snapshot_populated() {
processor.state.identifierText = "Insert cool identifier here"
assertSnapshots(
of: subject.navStackWrapped,
as: [.defaultPortrait, .defaultPortraitDark, .defaultPortraitAX5],
)
}
}

View File

@ -1,6 +1,6 @@
// swiftlint:disable:this file_name
import BitwardenResources
import SnapshotTesting
import ViewInspector
import XCTest
@testable import BitwardenShared
@ -29,71 +29,6 @@ class TwoFactorAuthViewTests: BitwardenTestCase {
subject = nil
}
// MARK: Tests
/// Tapping an auth method button dispatches the `.authMethodSelected()` action.
@MainActor
func test_authMethodButton_tap() throws {
processor.state.availableAuthMethods = [.recoveryCode]
let menu = try subject.inspect().find(ViewType.Menu.self, containing: Localizations.useAnotherTwoStepMethod)
let subMenu = try menu.find(ViewType.Menu.self, containing: Localizations.recoveryCodeTitle)
let button = try subMenu.find(button: Localizations.recoveryCodeTitle)
try button.tap()
XCTAssertEqual(processor.dispatchedActions.last, .authMethodSelected(.recoveryCode))
}
/// Tapping the cancel button dispatches the `.dismiss` action.
@MainActor
func test_cancelButton_tap() throws {
let button = try subject.inspect().findCancelToolbarButton()
try button.tap()
XCTAssertEqual(processor.dispatchedActions.last, .dismiss)
}
/// Tapping the continue button performs the `.continueTapped` effect.
@MainActor
func test_continueButton_tap() async throws {
let button = try subject.inspect().find(asyncButton: Localizations.continue)
try await button.tap()
XCTAssertEqual(processor.effects.last, .continueTapped)
}
/// Tapping the duo button performs the `.beginDuoAuth` effect.
@MainActor
func test_launchDuo_tap() async throws {
processor.state.authMethod = .duo
let button = try subject.inspect().find(asyncButton: Localizations.launchDuo)
try await button.tap()
XCTAssertEqual(processor.effects.last, .beginDuoAuth)
}
/// Changing the remember me toggle dispatches the `.rememberMeToggleChanged(_)` action.
@MainActor
func test_rememberMeToggle_changed() throws {
if #available(iOS 16.0, macOS 13.0, tvOS 16.0, watchOS 9.0, *) {
throw XCTSkip("Unable to run test in iOS 16, keep an eye on ViewInspector to see if it gets updated.")
}
let toggle = try subject.inspect().find(ViewType.Toggle.self)
try toggle.tap()
XCTAssertEqual(processor.dispatchedActions.last, .rememberMeToggleChanged(true))
}
/// Tapping the resend email button performs the `.resendEmailTapped` effect.
@MainActor
func test_resendEmailButton_tap() async throws {
let button = try subject.inspect().find(asyncButton: Localizations.resendCode)
try await button.tap()
XCTAssertEqual(processor.effects.last, .resendEmailTapped)
}
/// Updating the value in the verification code text field dispatches the `.verificationCodeChanged(_)` action.
@MainActor
func test_verificationCode_updateValue() throws {
let textField = try subject.inspect().find(textField: "")
try textField.setInput("text")
XCTAssertEqual(processor.dispatchedActions.last, .verificationCodeChanged("text"))
}
// MARK: Snapshots
/// The default view renders correctly for the authenticator app method.

View File

@ -0,0 +1,96 @@
// swiftlint:disable:this file_name
import BitwardenResources
import ViewInspector
import XCTest
@testable import BitwardenShared
class TwoFactorAuthViewTests: BitwardenTestCase {
// MARK: Properties
var processor: MockProcessor<TwoFactorAuthState, TwoFactorAuthAction, TwoFactorAuthEffect>!
var subject: TwoFactorAuthView!
// MARK: Setup & Teardown
override func setUp() {
super.setUp()
processor = MockProcessor(state: TwoFactorAuthState(displayEmail: "sh***@livefront.com"))
let store = Store(processor: processor)
subject = TwoFactorAuthView(store: store)
}
override func tearDown() {
super.tearDown()
processor = nil
subject = nil
}
// MARK: Tests
/// Tapping an auth method button dispatches the `.authMethodSelected()` action.
@MainActor
func test_authMethodButton_tap() throws {
processor.state.availableAuthMethods = [.recoveryCode]
let menu = try subject.inspect().find(ViewType.Menu.self, containing: Localizations.useAnotherTwoStepMethod)
let subMenu = try menu.find(ViewType.Menu.self, containing: Localizations.recoveryCodeTitle)
let button = try subMenu.find(button: Localizations.recoveryCodeTitle)
try button.tap()
XCTAssertEqual(processor.dispatchedActions.last, .authMethodSelected(.recoveryCode))
}
/// Tapping the cancel button dispatches the `.dismiss` action.
@MainActor
func test_cancelButton_tap() throws {
let button = try subject.inspect().findCancelToolbarButton()
try button.tap()
XCTAssertEqual(processor.dispatchedActions.last, .dismiss)
}
/// Tapping the continue button performs the `.continueTapped` effect.
@MainActor
func test_continueButton_tap() async throws {
let button = try subject.inspect().find(asyncButton: Localizations.continue)
try await button.tap()
XCTAssertEqual(processor.effects.last, .continueTapped)
}
/// Tapping the duo button performs the `.beginDuoAuth` effect.
@MainActor
func test_launchDuo_tap() async throws {
processor.state.authMethod = .duo
let button = try subject.inspect().find(asyncButton: Localizations.launchDuo)
try await button.tap()
XCTAssertEqual(processor.effects.last, .beginDuoAuth)
}
/// Changing the remember me toggle dispatches the `.rememberMeToggleChanged(_)` action.
@MainActor
func test_rememberMeToggle_changed() throws {
if #available(iOS 16.0, macOS 13.0, tvOS 16.0, watchOS 9.0, *) {
throw XCTSkip("Unable to run test in iOS 16, keep an eye on ViewInspector to see if it gets updated.")
}
let toggle = try subject.inspect().find(ViewType.Toggle.self)
try toggle.tap()
XCTAssertEqual(processor.dispatchedActions.last, .rememberMeToggleChanged(true))
}
/// Tapping the resend email button performs the `.resendEmailTapped` effect.
@MainActor
func test_resendEmailButton_tap() async throws {
let button = try subject.inspect().find(asyncButton: Localizations.resendCode)
try await button.tap()
XCTAssertEqual(processor.effects.last, .resendEmailTapped)
}
/// Updating the value in the verification code text field dispatches the `.verificationCodeChanged(_)` action.
@MainActor
func test_verificationCode_updateValue() throws {
let textField = try subject.inspect().find(textField: "")
try textField.setInput("text")
XCTAssertEqual(processor.dispatchedActions.last, .verificationCodeChanged("text"))
}
}

View File

@ -0,0 +1,46 @@
// swiftlint:disable:this file_name
import BitwardenResources
import SnapshotTesting
import XCTest
@testable import BitwardenShared
// MARK: - PasswordHintViewTests
class PasswordHintViewTests: BitwardenTestCase {
// MARK: Properties
var processor: MockProcessor<PasswordHintState, PasswordHintAction, PasswordHintEffect>!
var subject: PasswordHintView!
// MARK: Setup & Teardown
override func setUp() {
super.setUp()
let state = PasswordHintState()
processor = MockProcessor(state: state)
subject = PasswordHintView(store: Store(processor: processor))
}
override func tearDown() {
super.tearDown()
processor = nil
subject = nil
}
// MARK: Snapshots
/// A snapshot of the view without any values set.
@MainActor
func disabletest_snapshot_empty() {
processor.state.emailAddress = ""
assertSnapshot(of: subject.navStackWrapped, as: .defaultPortrait)
}
/// A snapshot of the view with a value in the email address field.
@MainActor
func disabletest_snapshot_withEmailAddress() {
processor.state.emailAddress = "email@example.com"
assertSnapshots(of: subject.navStackWrapped, as: [.defaultPortrait, .defaultPortraitAX5, .defaultPortraitDark])
}
}

View File

@ -1,5 +1,5 @@
// swiftlint:disable:this file_name
import BitwardenResources
import SnapshotTesting
import ViewInspector
import XCTest
@ -54,20 +54,4 @@ class PasswordHintViewTests: BitwardenTestCase {
try await button.tap()
XCTAssertEqual(processor.effects.last, .submitPressed)
}
// MARK: Snapshots
/// A snapshot of the view without any values set.
@MainActor
func disabletest_snapshot_empty() {
processor.state.emailAddress = ""
assertSnapshot(of: subject.navStackWrapped, as: .defaultPortrait)
}
/// A snapshot of the view with a value in the email address field.
@MainActor
func disabletest_snapshot_withEmailAddress() {
processor.state.emailAddress = "email@example.com"
assertSnapshots(of: subject.navStackWrapped, as: [.defaultPortrait, .defaultPortraitAX5, .defaultPortraitDark])
}
}

View File

@ -2,7 +2,6 @@ import BitwardenKit
import BitwardenKitMocks
import BitwardenResources
import BitwardenSdk
import SnapshotTesting
import SwiftUI
import XCTest

View File

@ -1,6 +1,6 @@
// swiftlint:disable:this file_name
import SnapshotTesting
import SwiftUI
import ViewInspector
import XCTest
@testable import BitwardenShared

View File

@ -0,0 +1,159 @@
// swiftlint:disable:this file_name
import BitwardenResources
import BitwardenSdk
import SnapshotTesting
import SwiftUI
import XCTest
@testable import BitwardenShared
// MARK: - ProfileSwitcherSheet Tests
class ProfileSwitcherSheetTests: BitwardenTestCase {
// MARK: Properties
var processor: MockProcessor<ProfileSwitcherState, ProfileSwitcherAction, ProfileSwitcherEffect>!
var subject: ProfileSwitcherSheet!
// MARK: Setup & Teardown
override func setUp() {
super.setUp()
let account = ProfileSwitcherItem.anneAccount
let state = ProfileSwitcherState(
accounts: [account],
activeAccountId: account.userId,
allowLockAndLogout: true,
isVisible: true,
)
processor = MockProcessor(state: state)
subject = ProfileSwitcherSheet(store: Store(processor: processor))
}
override func tearDown() {
super.tearDown()
processor = nil
subject = nil
}
// MARK: Snapshots
// NB: There's not really a good way, it seems, to capture a view hierarchy when it's presenting a sheet.
// cf. https://github.com/pointfreeco/swift-snapshot-testing/discussions/956
func disabletest_snapshot_singleAccount() {
assertSnapshot(of: NavigationView { subject }, as: .defaultPortrait)
}
@MainActor
func disabletest_snapshot_multiAccount_unlocked_belowMaximum() {
processor.state = ProfileSwitcherState(
accounts: [
ProfileSwitcherItem.anneAccount,
ProfileSwitcherItem.fixture(
color: .yellow,
email: "bonus.bridge@bitwarden.com",
isUnlocked: true,
userInitials: "BB",
),
ProfileSwitcherItem.fixture(
color: .teal,
email: "concurrent.claim@bitarden.com",
isUnlocked: true,
userInitials: "CC",
),
ProfileSwitcherItem.fixture(
color: .indigo,
email: "double.dip@bitwarde.com",
isUnlocked: true,
userInitials: "DD",
),
],
activeAccountId: ProfileSwitcherItem.anneAccount.userId,
allowLockAndLogout: true,
isVisible: true,
)
assertSnapshot(of: NavigationView { subject }, as: .defaultPortrait)
}
@MainActor
func disabletest_snapshot_multiAccount_unlocked_atMaximum() {
processor.state = ProfileSwitcherState.maximumAccounts
assertSnapshot(of: NavigationView { subject }, as: .defaultPortrait)
}
@MainActor
func disabletest_snapshot_multiAccount_unlocked_atMaximum_largeText() {
processor.state = ProfileSwitcherState.maximumAccounts
assertSnapshot(of: NavigationView { subject }, as: .defaultPortraitAX5)
}
@MainActor
func disabletest_snapshot_multiAccount_locked_belowMaximum() {
processor.state = ProfileSwitcherState(
accounts: [
ProfileSwitcherItem.fixture(
color: .yellow,
email: "bonus.bridge@bitwarden.com",
isUnlocked: false,
userInitials: "BB",
),
ProfileSwitcherItem.fixture(
color: .teal,
email: "concurrent.claim@bitarden.com",
isUnlocked: false,
userInitials: "CC",
),
ProfileSwitcherItem.anneAccount,
ProfileSwitcherItem.fixture(
color: .indigo,
email: "double.dip@bitwarde.com",
isUnlocked: false,
userInitials: "DD",
),
],
activeAccountId: ProfileSwitcherItem.anneAccount.userId,
allowLockAndLogout: true,
isVisible: true,
)
assertSnapshot(of: NavigationView { subject }, as: .defaultPortrait)
}
@MainActor
func disabletest_snapshot_multiAccount_locked_atMaximum() {
processor.state = ProfileSwitcherState(
accounts: [
ProfileSwitcherItem.fixture(
color: .yellow,
email: "bonus.bridge@bitwarden.com",
isUnlocked: false,
userInitials: "BB",
),
ProfileSwitcherItem.fixture(
color: .teal,
email: "concurrent.claim@bitarden.com",
isUnlocked: false,
userInitials: "CC",
),
.anneAccount,
ProfileSwitcherItem.fixture(
color: .indigo,
email: "double.dip@bitwarde.com",
isUnlocked: false,
userInitials: "DD",
),
ProfileSwitcherItem.fixture(
color: .green,
email: "extra.edition@bitwarden.com",
isUnlocked: false,
userInitials: "EE",
),
],
activeAccountId: ProfileSwitcherItem.anneAccount.userId,
allowLockAndLogout: true,
isVisible: true,
)
assertSnapshot(of: NavigationView { subject }, as: .defaultPortrait)
}
}

View File

@ -0,0 +1,193 @@
// swiftlint:disable:this file_name
import BitwardenResources
import BitwardenSdk
import SwiftUI
import ViewInspector
import XCTest
@testable import BitwardenShared
// MARK: - ProfileSwitcherSheet Tests
class ProfileSwitcherSheetTests: BitwardenTestCase {
// MARK: Properties
var processor: MockProcessor<ProfileSwitcherState, ProfileSwitcherAction, ProfileSwitcherEffect>!
var subject: ProfileSwitcherSheet!
// MARK: Setup & Teardown
override func setUp() {
super.setUp()
let account = ProfileSwitcherItem.anneAccount
let state = ProfileSwitcherState(
accounts: [account],
activeAccountId: account.userId,
allowLockAndLogout: true,
isVisible: true,
)
processor = MockProcessor(state: state)
subject = ProfileSwitcherSheet(store: Store(processor: processor))
}
override func tearDown() {
super.tearDown()
processor = nil
subject = nil
}
// MARK: Tests
/// Long pressing a profile row dispatches the `.accountLongPressed` action.
@MainActor
func test_accountRow_longPress_currentAccount() throws {
let accountRow = try subject.inspect().find(button: "anne.account@bitwarden.com")
try accountRow.labelView().callOnLongPressGesture()
let currentAccount = processor.state.activeAccountProfile!
waitFor(!processor.effects.isEmpty)
XCTAssertEqual(processor.effects.last, .accountLongPressed(currentAccount))
}
/// Long pressing is disabled if lock and logout are not available.
@MainActor
func test_accountRow_longPress_currentAccount_noLockOrLogout() throws {
processor.state.allowLockAndLogout = false
processor.state.accounts[0].canBeLocked = false
let accountRow = try subject.inspect().find(button: "anne.account@bitwarden.com")
XCTAssertThrowsError(try accountRow.labelView().callOnLongPressGesture())
}
/// Tapping a profile row dispatches the `.accountPressed` action.
@MainActor
func test_accountRow_tap_currentAccount() throws {
let accountRow = try subject.inspect().find(button: "anne.account@bitwarden.com")
try accountRow.labelView().callOnTapGesture()
let currentAccount = processor.state.activeAccountProfile!
waitFor(!processor.effects.isEmpty)
XCTAssertEqual(processor.effects.last, .accountPressed(currentAccount))
}
/// Tapping a profile row dispatches the `.accountPressed` action.
@MainActor
func test_accountRow_tap_addAccount() throws {
let addAccountRow = try subject.inspect().find(button: "Add account")
try addAccountRow.tap()
waitFor(!processor.effects.isEmpty)
XCTAssertEqual(processor.effects.last, .addAccountPressed)
}
/// Long pressing an alternative profile row dispatches the `.accountLongPressed` action.
@MainActor
func test_alternateAccountRow_longPress_alternateAccount() throws {
let alternate = ProfileSwitcherItem.fixture(
email: "alternate@bitwarden.com",
userInitials: "NA",
)
let current = processor.state.activeAccountProfile!
processor.state = ProfileSwitcherState(
accounts: [
alternate,
current,
],
activeAccountId: current.userId,
allowLockAndLogout: true,
isVisible: true,
)
let alternateRow = try subject.inspect().find(button: "alternate@bitwarden.com")
try alternateRow.labelView().callOnLongPressGesture()
waitFor(!processor.effects.isEmpty)
XCTAssertEqual(processor.effects.last, .accountLongPressed(alternate))
}
/// Long pressing is disabled if lock and logout are not available.
@MainActor
func test_alternateAccountRow_longPress_currentAccount_noLockOrLogout() throws {
let alternate = ProfileSwitcherItem.fixture(
canBeLocked: false,
email: "alternate@bitwarden.com",
userInitials: "NA",
)
let current = processor.state.activeAccountProfile!
processor.state = ProfileSwitcherState(
accounts: [
alternate,
current,
],
activeAccountId: current.userId,
allowLockAndLogout: false,
isVisible: true,
)
let alternateRow = try subject.inspect().find(button: "alternate@bitwarden.com")
_ = try subject.inspect().find(button: "anne.account@bitwarden.com")
XCTAssertThrowsError(try alternateRow.labelView().callOnLongPressGesture())
}
/// Tapping an alternative profile row dispatches the `.accountPressed` action.
@MainActor
func test_alternateAccountRow_tap_alternateAccount() throws {
let alternate = ProfileSwitcherItem.fixture(
email: "alternate@bitwarden.com",
userInitials: "NA",
)
let current = processor.state.activeAccountProfile!
processor.state = ProfileSwitcherState(
accounts: [
alternate,
current,
],
activeAccountId: current.userId,
allowLockAndLogout: true,
isVisible: true,
)
let alternateRow = try subject.inspect().find(button: "alternate@bitwarden.com")
try alternateRow.labelView().callOnTapGesture()
waitFor(!processor.effects.isEmpty)
XCTAssertEqual(processor.effects.last, .accountPressed(alternate))
}
/// Tapping an alternative profile row dispatches the `.accountPressed` action.
@MainActor
func test_alternateAccountRows_tap_alternateEmptyAccount() throws {
let alternate = ProfileSwitcherItem.fixture(
email: "locked@bitwarden.com",
isUnlocked: false,
userInitials: "LA",
)
let secondAlternate = ProfileSwitcherItem.fixture()
let alternateAccounts = [
alternate,
secondAlternate,
]
let current = processor.state.activeAccountProfile!
processor.state = ProfileSwitcherState(
accounts: alternateAccounts + [current],
activeAccountId: current.userId,
allowLockAndLogout: true,
isVisible: true,
)
let secondAlternateRow = try subject.inspect().find(button: "")
try secondAlternateRow.labelView().callOnTapGesture()
waitFor(!processor.effects.isEmpty)
XCTAssertEqual(processor.effects.last, .accountPressed(secondAlternate))
}
/// The close toolbar button closes the sheet.
@MainActor
func test_closeToolbarButton() throws {
let closeButton = try subject.inspect().findCloseToolbarButton()
try closeButton.tap()
XCTAssertEqual(processor.dispatchedActions.last, .dismissTapped)
}
// MARK: Snapshots
// NB: There's not really a good way, it seems, to capture a view hierarchy when it's presenting a sheet.
// cf. https://github.com/pointfreeco/swift-snapshot-testing/discussions/956
}

View File

@ -1,16 +1,13 @@
import BitwardenResources
import BitwardenSdk
import SnapshotTesting
import SwiftUI
import ViewInspector
import XCTest
@testable import BitwardenShared
// MARK: - ProfileSwitcherSheet Tests
class ProfileSwitcherSheetTests: BitwardenTestCase { // swiftlint:disable:this type_body_length
class ProfileSwitcherSheetTests: BitwardenTestCase {
// MARK: Properties
var processor: MockProcessor<ProfileSwitcherState, ProfileSwitcherAction, ProfileSwitcherEffect>!
@ -40,145 +37,6 @@ class ProfileSwitcherSheetTests: BitwardenTestCase { // swiftlint:disable:this t
// MARK: Tests
/// Long pressing a profile row dispatches the `.accountLongPressed` action.
@MainActor
func test_accountRow_longPress_currentAccount() throws {
let accountRow = try subject.inspect().find(button: "anne.account@bitwarden.com")
try accountRow.labelView().callOnLongPressGesture()
let currentAccount = processor.state.activeAccountProfile!
waitFor(!processor.effects.isEmpty)
XCTAssertEqual(processor.effects.last, .accountLongPressed(currentAccount))
}
/// Long pressing is disabled if lock and logout are not available.
@MainActor
func test_accountRow_longPress_currentAccount_noLockOrLogout() throws {
processor.state.allowLockAndLogout = false
processor.state.accounts[0].canBeLocked = false
let accountRow = try subject.inspect().find(button: "anne.account@bitwarden.com")
XCTAssertThrowsError(try accountRow.labelView().callOnLongPressGesture())
}
/// Tapping a profile row dispatches the `.accountPressed` action.
@MainActor
func test_accountRow_tap_currentAccount() throws {
let accountRow = try subject.inspect().find(button: "anne.account@bitwarden.com")
try accountRow.labelView().callOnTapGesture()
let currentAccount = processor.state.activeAccountProfile!
waitFor(!processor.effects.isEmpty)
XCTAssertEqual(processor.effects.last, .accountPressed(currentAccount))
}
/// Tapping a profile row dispatches the `.accountPressed` action.
@MainActor
func test_accountRow_tap_addAccount() throws {
let addAccountRow = try subject.inspect().find(button: "Add account")
try addAccountRow.tap()
waitFor(!processor.effects.isEmpty)
XCTAssertEqual(processor.effects.last, .addAccountPressed)
}
/// Long pressing an alternative profile row dispatches the `.accountLongPressed` action.
@MainActor
func test_alternateAccountRow_longPress_alternateAccount() throws {
let alternate = ProfileSwitcherItem.fixture(
email: "alternate@bitwarden.com",
userInitials: "NA",
)
let current = processor.state.activeAccountProfile!
processor.state = ProfileSwitcherState(
accounts: [
alternate,
current,
],
activeAccountId: current.userId,
allowLockAndLogout: true,
isVisible: true,
)
let alternateRow = try subject.inspect().find(button: "alternate@bitwarden.com")
try alternateRow.labelView().callOnLongPressGesture()
waitFor(!processor.effects.isEmpty)
XCTAssertEqual(processor.effects.last, .accountLongPressed(alternate))
}
/// Long pressing is disabled if lock and logout are not available.
@MainActor
func test_alternateAccountRow_longPress_currentAccount_noLockOrLogout() throws {
let alternate = ProfileSwitcherItem.fixture(
canBeLocked: false,
email: "alternate@bitwarden.com",
userInitials: "NA",
)
let current = processor.state.activeAccountProfile!
processor.state = ProfileSwitcherState(
accounts: [
alternate,
current,
],
activeAccountId: current.userId,
allowLockAndLogout: false,
isVisible: true,
)
let alternateRow = try subject.inspect().find(button: "alternate@bitwarden.com")
_ = try subject.inspect().find(button: "anne.account@bitwarden.com")
XCTAssertThrowsError(try alternateRow.labelView().callOnLongPressGesture())
}
/// Tapping an alternative profile row dispatches the `.accountPressed` action.
@MainActor
func test_alternateAccountRow_tap_alternateAccount() throws {
let alternate = ProfileSwitcherItem.fixture(
email: "alternate@bitwarden.com",
userInitials: "NA",
)
let current = processor.state.activeAccountProfile!
processor.state = ProfileSwitcherState(
accounts: [
alternate,
current,
],
activeAccountId: current.userId,
allowLockAndLogout: true,
isVisible: true,
)
let alternateRow = try subject.inspect().find(button: "alternate@bitwarden.com")
try alternateRow.labelView().callOnTapGesture()
waitFor(!processor.effects.isEmpty)
XCTAssertEqual(processor.effects.last, .accountPressed(alternate))
}
/// Tapping an alternative profile row dispatches the `.accountPressed` action.
@MainActor
func test_alternateAccountRows_tap_alternateEmptyAccount() throws {
let alternate = ProfileSwitcherItem.fixture(
email: "locked@bitwarden.com",
isUnlocked: false,
userInitials: "LA",
)
let secondAlternate = ProfileSwitcherItem.fixture()
let alternateAccounts = [
alternate,
secondAlternate,
]
let current = processor.state.activeAccountProfile!
processor.state = ProfileSwitcherState(
accounts: alternateAccounts + [current],
activeAccountId: current.userId,
allowLockAndLogout: true,
isVisible: true,
)
let secondAlternateRow = try subject.inspect().find(button: "")
try secondAlternateRow.labelView().callOnTapGesture()
waitFor(!processor.effects.isEmpty)
XCTAssertEqual(processor.effects.last, .accountPressed(secondAlternate))
}
/// Tests the add account visibility below the maximum account limit
@MainActor
func test_addAccountRow_subMaximumAccounts_showAdd() throws {
@ -226,132 +84,4 @@ class ProfileSwitcherSheetTests: BitwardenTestCase { // swiftlint:disable:this t
processor.state = ProfileSwitcherState.maximumAccounts
XCTAssertFalse(subject.store.state.showsAddAccount)
}
/// The close toolbar button closes the sheet.
@MainActor
func test_closeToolbarButton() throws {
let closeButton = try subject.inspect().findCloseToolbarButton()
try closeButton.tap()
XCTAssertEqual(processor.dispatchedActions.last, .dismissTapped)
}
// MARK: Snapshots
// NB: There's not really a good way, it seems, to capture a view hierarchy when it's presenting a sheet.
// cf. https://github.com/pointfreeco/swift-snapshot-testing/discussions/956
func disabletest_snapshot_singleAccount() {
assertSnapshot(of: NavigationView { subject }, as: .defaultPortrait)
}
@MainActor
func disabletest_snapshot_multiAccount_unlocked_belowMaximum() {
processor.state = ProfileSwitcherState(
accounts: [
ProfileSwitcherItem.anneAccount,
ProfileSwitcherItem.fixture(
color: .yellow,
email: "bonus.bridge@bitwarden.com",
isUnlocked: true,
userInitials: "BB",
),
ProfileSwitcherItem.fixture(
color: .teal,
email: "concurrent.claim@bitarden.com",
isUnlocked: true,
userInitials: "CC",
),
ProfileSwitcherItem.fixture(
color: .indigo,
email: "double.dip@bitwarde.com",
isUnlocked: true,
userInitials: "DD",
),
],
activeAccountId: ProfileSwitcherItem.anneAccount.userId,
allowLockAndLogout: true,
isVisible: true,
)
assertSnapshot(of: NavigationView { subject }, as: .defaultPortrait)
}
@MainActor
func disabletest_snapshot_multiAccount_unlocked_atMaximum() {
processor.state = ProfileSwitcherState.maximumAccounts
assertSnapshot(of: NavigationView { subject }, as: .defaultPortrait)
}
@MainActor
func disabletest_snapshot_multiAccount_unlocked_atMaximum_largeText() {
processor.state = ProfileSwitcherState.maximumAccounts
assertSnapshot(of: NavigationView { subject }, as: .defaultPortraitAX5)
}
@MainActor
func disabletest_snapshot_multiAccount_locked_belowMaximum() {
processor.state = ProfileSwitcherState(
accounts: [
ProfileSwitcherItem.fixture(
color: .yellow,
email: "bonus.bridge@bitwarden.com",
isUnlocked: false,
userInitials: "BB",
),
ProfileSwitcherItem.fixture(
color: .teal,
email: "concurrent.claim@bitarden.com",
isUnlocked: false,
userInitials: "CC",
),
ProfileSwitcherItem.anneAccount,
ProfileSwitcherItem.fixture(
color: .indigo,
email: "double.dip@bitwarde.com",
isUnlocked: false,
userInitials: "DD",
),
],
activeAccountId: ProfileSwitcherItem.anneAccount.userId,
allowLockAndLogout: true,
isVisible: true,
)
assertSnapshot(of: NavigationView { subject }, as: .defaultPortrait)
}
@MainActor
func disabletest_snapshot_multiAccount_locked_atMaximum() {
processor.state = ProfileSwitcherState(
accounts: [
ProfileSwitcherItem.fixture(
color: .yellow,
email: "bonus.bridge@bitwarden.com",
isUnlocked: false,
userInitials: "BB",
),
ProfileSwitcherItem.fixture(
color: .teal,
email: "concurrent.claim@bitarden.com",
isUnlocked: false,
userInitials: "CC",
),
.anneAccount,
ProfileSwitcherItem.fixture(
color: .indigo,
email: "double.dip@bitwarde.com",
isUnlocked: false,
userInitials: "DD",
),
ProfileSwitcherItem.fixture(
color: .green,
email: "extra.edition@bitwarden.com",
isUnlocked: false,
userInitials: "EE",
),
],
activeAccountId: ProfileSwitcherItem.anneAccount.userId,
allowLockAndLogout: true,
isVisible: true,
)
assertSnapshot(of: NavigationView { subject }, as: .defaultPortrait)
}
}

View File

@ -1,3 +1,4 @@
// swiftlint:disable:this file_name
import BitwardenResources
import BitwardenSdk
import SnapshotTesting
@ -45,18 +46,6 @@ final class ProfileSwitcherToolbarViewTests: BitwardenTestCase {
}
}
/// Tapping the view dispatches the `.requestedProfileSwitcher` effect.
@MainActor
func test_tap_currentAccount() async throws {
let view = try subject.inspect().find(asyncButtonWithAccessibilityLabel: Localizations.account)
try await view.tap()
XCTAssertEqual(
processor.effects.last,
.requestedProfileSwitcher(visible: !subject.store.state.isVisible),
)
}
// MARK: Snapshots
@MainActor

View File

@ -0,0 +1,59 @@
// swiftlint:disable:this file_name
import BitwardenResources
import BitwardenSdk
import SwiftUI
import XCTest
@testable import BitwardenShared
final class ProfileSwitcherToolbarViewTests: BitwardenTestCase {
var processor: MockProcessor<ProfileSwitcherState, ProfileSwitcherAction, ProfileSwitcherEffect>!
var subject: ProfileSwitcherToolbarView!
// MARK: Setup & Teardown
override func setUp() {
super.setUp()
let account = ProfileSwitcherItem.anneAccount
let state = ProfileSwitcherState(
accounts: [account],
activeAccountId: account.userId,
allowLockAndLogout: true,
isVisible: true,
)
processor = MockProcessor(state: state)
subject = ProfileSwitcherToolbarView(store: Store(processor: processor))
}
override func tearDown() {
super.tearDown()
processor = nil
subject = nil
}
@ViewBuilder
func snapshotSubject(title: String) -> some View {
NavigationView {
Spacer()
.navigationBarTitle(title, displayMode: .inline)
.toolbar {
ToolbarItem(placement: .navigationBarLeading) {
subject
}
}
}
}
/// Tapping the view dispatches the `.requestedProfileSwitcher` effect.
@MainActor
func test_tap_currentAccount() async throws {
let view = try subject.inspect().find(asyncButtonWithAccessibilityLabel: Localizations.account)
try await view.tap()
XCTAssertEqual(
processor.effects.last,
.requestedProfileSwitcher(visible: !subject.store.state.isVisible),
)
}
}

View File

@ -0,0 +1,165 @@
// swiftlint:disable:this file_name
import BitwardenSdk
import SnapshotTesting
import SwiftUI
import XCTest
@testable import BitwardenShared
// MARK: - ProfileSwitcherViewTests
class ProfileSwitcherViewTests: BitwardenTestCase {
// MARK: Properties
var processor: MockProcessor<ProfileSwitcherState, ProfileSwitcherAction, ProfileSwitcherEffect>!
var subject: ProfileSwitcherView!
// MARK: Setup & Teardown
override func setUp() {
super.setUp()
let account = ProfileSwitcherItem.anneAccount
let state = ProfileSwitcherState(
accounts: [account],
activeAccountId: account.userId,
allowLockAndLogout: true,
isVisible: true,
)
processor = MockProcessor(state: state)
subject = ProfileSwitcherView(store: Store(processor: processor))
}
override func tearDown() {
super.tearDown()
processor = nil
subject = nil
}
// MARK: Snapshots
func disabletest_snapshot_singleAccount() {
assertSnapshot(of: subject, as: .defaultPortrait)
}
@MainActor
func disabletest_snapshot_multiAccount_unlocked_belowMaximum() {
processor.state = ProfileSwitcherState(
accounts: [
ProfileSwitcherItem.anneAccount,
ProfileSwitcherItem.fixture(
color: .yellow,
email: "bonus.bridge@bitwarden.com",
isUnlocked: true,
userInitials: "BB",
),
ProfileSwitcherItem.fixture(
color: .teal,
email: "concurrent.claim@bitarden.com",
isUnlocked: true,
userInitials: "CC",
),
ProfileSwitcherItem.fixture(
color: .indigo,
email: "double.dip@bitwarde.com",
isUnlocked: true,
userInitials: "DD",
),
],
activeAccountId: ProfileSwitcherItem.anneAccount.userId,
allowLockAndLogout: true,
isVisible: true,
)
assertSnapshot(of: subject, as: .defaultPortrait)
}
@MainActor
func disabletest_snapshot_multiAccount_unlocked_atMaximum() {
processor.state = ProfileSwitcherState.maximumAccounts
assertSnapshot(of: subject, as: .defaultPortrait)
}
@MainActor
func disabletest_snapshot_multiAccount_unlocked_atMaximum_largeText() {
processor.state = ProfileSwitcherState.maximumAccounts
assertSnapshot(of: subject, as: .defaultPortraitAX5)
}
@MainActor
func disabletest_snapshot_multiAccount_locked_belowMaximum() {
processor.state = ProfileSwitcherState(
accounts: [
ProfileSwitcherItem.fixture(
color: .yellow,
email: "bonus.bridge@bitwarden.com",
isUnlocked: false,
userInitials: "BB",
),
ProfileSwitcherItem.fixture(
color: .teal,
email: "concurrent.claim@bitarden.com",
isUnlocked: false,
userInitials: "CC",
),
ProfileSwitcherItem.anneAccount,
ProfileSwitcherItem.fixture(
color: .indigo,
email: "double.dip@bitwarde.com",
isUnlocked: false,
userInitials: "DD",
),
],
activeAccountId: ProfileSwitcherItem.anneAccount.userId,
allowLockAndLogout: true,
isVisible: true,
)
assertSnapshot(of: subject, as: .defaultPortrait)
}
@MainActor
func disabletest_snapshot_multiAccount_locked_atMaximum() {
processor.state = ProfileSwitcherState(
accounts: [
ProfileSwitcherItem.fixture(
color: .yellow,
email: "bonus.bridge@bitwarden.com",
isUnlocked: false,
userInitials: "BB",
),
ProfileSwitcherItem.fixture(
color: .teal,
email: "concurrent.claim@bitarden.com",
isUnlocked: false,
userInitials: "CC",
),
.anneAccount,
ProfileSwitcherItem.fixture(
color: .indigo,
email: "double.dip@bitwarde.com",
isUnlocked: false,
userInitials: "DD",
),
ProfileSwitcherItem.fixture(
color: .green,
email: "extra.edition@bitwarden.com",
isUnlocked: false,
userInitials: "EE",
),
],
activeAccountId: ProfileSwitcherItem.anneAccount.userId,
allowLockAndLogout: true,
isVisible: true,
)
assertSnapshot(of: subject, as: .defaultPortrait)
}
/// Test a snapshot of the ProfileSwitcherView previews.
func disabletest_snapshot_profileSwitcherView_previews() {
for preview in ProfileSwitcherView_Previews._allPreviews {
assertSnapshots(
of: preview.content,
as: [.defaultPortrait],
)
}
}
}

View File

@ -0,0 +1,189 @@
// swiftlint:disable:this file_name
import BitwardenSdk
import SwiftUI
import ViewInspector
import XCTest
@testable import BitwardenShared
// MARK: - ProfileSwitcherViewTests
class ProfileSwitcherViewTests: BitwardenTestCase {
// MARK: Properties
var processor: MockProcessor<ProfileSwitcherState, ProfileSwitcherAction, ProfileSwitcherEffect>!
var subject: ProfileSwitcherView!
// MARK: Setup & Teardown
override func setUp() {
super.setUp()
let account = ProfileSwitcherItem.anneAccount
let state = ProfileSwitcherState(
accounts: [account],
activeAccountId: account.userId,
allowLockAndLogout: true,
isVisible: true,
)
processor = MockProcessor(state: state)
subject = ProfileSwitcherView(store: Store(processor: processor))
}
override func tearDown() {
super.tearDown()
processor = nil
subject = nil
}
// MARK: Tests
/// Long pressing a profile row dispatches the `.accountLongPressed` action.
@MainActor
func test_accountRow_longPress_currentAccount() throws {
let accountRow = try subject.inspect().find(button: "anne.account@bitwarden.com")
try accountRow.labelView().callOnLongPressGesture()
let currentAccount = processor.state.activeAccountProfile!
waitFor(!processor.effects.isEmpty)
XCTAssertEqual(processor.effects.last, .accountLongPressed(currentAccount))
}
/// Long pressing is disabled if lock and logout are not available.
@MainActor
func test_accountRow_longPress_currentAccount_noLockOrLogout() throws {
processor.state.allowLockAndLogout = false
processor.state.accounts[0].canBeLocked = false
let accountRow = try subject.inspect().find(button: "anne.account@bitwarden.com")
XCTAssertThrowsError(try accountRow.labelView().callOnLongPressGesture())
}
/// Tapping a profile row dispatches the `.accountPressed` action.
@MainActor
func test_accountRow_tap_currentAccount() throws {
let accountRow = try subject.inspect().find(button: "anne.account@bitwarden.com")
try accountRow.labelView().callOnTapGesture()
let currentAccount = processor.state.activeAccountProfile!
waitFor(!processor.effects.isEmpty)
XCTAssertEqual(processor.effects.last, .accountPressed(currentAccount))
}
/// Tapping a profile row dispatches the `.accountPressed` action.
@MainActor
func test_accountRow_tap_addAccount() throws {
let addAccountRow = try subject.inspect().find(button: "Add account")
try addAccountRow.tap()
waitFor(!processor.effects.isEmpty)
XCTAssertEqual(processor.effects.last, .addAccountPressed)
}
/// Long pressing an alternative profile row dispatches the `.accountLongPressed` action.
@MainActor
func test_alternateAccountRow_longPress_alternateAccount() throws {
let alternate = ProfileSwitcherItem.fixture(
email: "alternate@bitwarden.com",
userInitials: "NA",
)
let current = processor.state.activeAccountProfile!
processor.state = ProfileSwitcherState(
accounts: [
alternate,
current,
],
activeAccountId: current.userId,
allowLockAndLogout: true,
isVisible: true,
)
let alternateRow = try subject.inspect().find(button: "alternate@bitwarden.com")
try alternateRow.labelView().callOnLongPressGesture()
waitFor(!processor.effects.isEmpty)
XCTAssertEqual(processor.effects.last, .accountLongPressed(alternate))
}
/// Long pressing is disabled if lock and logout are not available.
@MainActor
func test_alternateAccountRow_longPress_currentAccount_noLockOrLogout() throws {
let alternate = ProfileSwitcherItem.fixture(
canBeLocked: false,
email: "alternate@bitwarden.com",
userInitials: "NA",
)
let current = processor.state.activeAccountProfile!
processor.state = ProfileSwitcherState(
accounts: [
alternate,
current,
],
activeAccountId: current.userId,
allowLockAndLogout: false,
isVisible: true,
)
let alternateRow = try subject.inspect().find(button: "alternate@bitwarden.com")
_ = try subject.inspect().find(button: "anne.account@bitwarden.com")
XCTAssertThrowsError(try alternateRow.labelView().callOnLongPressGesture())
}
/// Tapping an alternative profile row dispatches the `.accountPressed` action.
@MainActor
func test_alternateAccountRow_tap_alternateAccount() throws {
let alternate = ProfileSwitcherItem.fixture(
email: "alternate@bitwarden.com",
userInitials: "NA",
)
let current = processor.state.activeAccountProfile!
processor.state = ProfileSwitcherState(
accounts: [
alternate,
current,
],
activeAccountId: current.userId,
allowLockAndLogout: true,
isVisible: true,
)
let alternateRow = try subject.inspect().find(button: "alternate@bitwarden.com")
try alternateRow.labelView().callOnTapGesture()
waitFor(!processor.effects.isEmpty)
XCTAssertEqual(processor.effects.last, .accountPressed(alternate))
}
/// Tapping an alternative profile row dispatches the `.accountPressed` action.
@MainActor
func test_alternateAccountRows_tap_alternateEmptyAccount() throws {
let alternate = ProfileSwitcherItem.fixture(
email: "locked@bitwarden.com",
isUnlocked: false,
userInitials: "LA",
)
let secondAlternate = ProfileSwitcherItem.fixture()
let alternateAccounts = [
alternate,
secondAlternate,
]
let current = processor.state.activeAccountProfile!
processor.state = ProfileSwitcherState(
accounts: alternateAccounts + [current],
activeAccountId: current.userId,
allowLockAndLogout: true,
isVisible: true,
)
let secondAlternateRow = try subject.inspect().find(button: "")
try secondAlternateRow.labelView().callOnTapGesture()
waitFor(!processor.effects.isEmpty)
XCTAssertEqual(processor.effects.last, .accountPressed(secondAlternate))
}
/// Tapping the background triggers a `.backgroundPressed` action.
@MainActor
func test_background_tap() throws {
let view = try subject.inspect().view(ProfileSwitcherView.self)
let background = view.first
try background?.callOnTapGesture()
XCTAssertEqual(processor.dispatchedActions.last, .backgroundTapped)
}
}

View File

@ -1,14 +1,12 @@
import BitwardenSdk
import SnapshotTesting
import SwiftUI
import ViewInspector
import XCTest
@testable import BitwardenShared
// MARK: - ProfileSwitcherViewTests
class ProfileSwitcherViewTests: BitwardenTestCase { // swiftlint:disable:this type_body_length
class ProfileSwitcherViewTests: BitwardenTestCase {
// MARK: Properties
var processor: MockProcessor<ProfileSwitcherState, ProfileSwitcherAction, ProfileSwitcherEffect>!
@ -38,155 +36,6 @@ class ProfileSwitcherViewTests: BitwardenTestCase { // swiftlint:disable:this ty
// MARK: Tests
/// Long pressing a profile row dispatches the `.accountLongPressed` action.
@MainActor
func test_accountRow_longPress_currentAccount() throws {
let accountRow = try subject.inspect().find(button: "anne.account@bitwarden.com")
try accountRow.labelView().callOnLongPressGesture()
let currentAccount = processor.state.activeAccountProfile!
waitFor(!processor.effects.isEmpty)
XCTAssertEqual(processor.effects.last, .accountLongPressed(currentAccount))
}
/// Long pressing is disabled if lock and logout are not available.
@MainActor
func test_accountRow_longPress_currentAccount_noLockOrLogout() throws {
processor.state.allowLockAndLogout = false
processor.state.accounts[0].canBeLocked = false
let accountRow = try subject.inspect().find(button: "anne.account@bitwarden.com")
XCTAssertThrowsError(try accountRow.labelView().callOnLongPressGesture())
}
/// Tapping a profile row dispatches the `.accountPressed` action.
@MainActor
func test_accountRow_tap_currentAccount() throws {
let accountRow = try subject.inspect().find(button: "anne.account@bitwarden.com")
try accountRow.labelView().callOnTapGesture()
let currentAccount = processor.state.activeAccountProfile!
waitFor(!processor.effects.isEmpty)
XCTAssertEqual(processor.effects.last, .accountPressed(currentAccount))
}
/// Tapping a profile row dispatches the `.accountPressed` action.
@MainActor
func test_accountRow_tap_addAccount() throws {
let addAccountRow = try subject.inspect().find(button: "Add account")
try addAccountRow.tap()
waitFor(!processor.effects.isEmpty)
XCTAssertEqual(processor.effects.last, .addAccountPressed)
}
/// Long pressing an alternative profile row dispatches the `.accountLongPressed` action.
@MainActor
func test_alternateAccountRow_longPress_alternateAccount() throws {
let alternate = ProfileSwitcherItem.fixture(
email: "alternate@bitwarden.com",
userInitials: "NA",
)
let current = processor.state.activeAccountProfile!
processor.state = ProfileSwitcherState(
accounts: [
alternate,
current,
],
activeAccountId: current.userId,
allowLockAndLogout: true,
isVisible: true,
)
let alternateRow = try subject.inspect().find(button: "alternate@bitwarden.com")
try alternateRow.labelView().callOnLongPressGesture()
waitFor(!processor.effects.isEmpty)
XCTAssertEqual(processor.effects.last, .accountLongPressed(alternate))
}
/// Long pressing is disabled if lock and logout are not available.
@MainActor
func test_alternateAccountRow_longPress_currentAccount_noLockOrLogout() throws {
let alternate = ProfileSwitcherItem.fixture(
canBeLocked: false,
email: "alternate@bitwarden.com",
userInitials: "NA",
)
let current = processor.state.activeAccountProfile!
processor.state = ProfileSwitcherState(
accounts: [
alternate,
current,
],
activeAccountId: current.userId,
allowLockAndLogout: false,
isVisible: true,
)
let alternateRow = try subject.inspect().find(button: "alternate@bitwarden.com")
_ = try subject.inspect().find(button: "anne.account@bitwarden.com")
XCTAssertThrowsError(try alternateRow.labelView().callOnLongPressGesture())
}
/// Tapping an alternative profile row dispatches the `.accountPressed` action.
@MainActor
func test_alternateAccountRow_tap_alternateAccount() throws {
let alternate = ProfileSwitcherItem.fixture(
email: "alternate@bitwarden.com",
userInitials: "NA",
)
let current = processor.state.activeAccountProfile!
processor.state = ProfileSwitcherState(
accounts: [
alternate,
current,
],
activeAccountId: current.userId,
allowLockAndLogout: true,
isVisible: true,
)
let alternateRow = try subject.inspect().find(button: "alternate@bitwarden.com")
try alternateRow.labelView().callOnTapGesture()
waitFor(!processor.effects.isEmpty)
XCTAssertEqual(processor.effects.last, .accountPressed(alternate))
}
/// Tapping an alternative profile row dispatches the `.accountPressed` action.
@MainActor
func test_alternateAccountRows_tap_alternateEmptyAccount() throws {
let alternate = ProfileSwitcherItem.fixture(
email: "locked@bitwarden.com",
isUnlocked: false,
userInitials: "LA",
)
let secondAlternate = ProfileSwitcherItem.fixture()
let alternateAccounts = [
alternate,
secondAlternate,
]
let current = processor.state.activeAccountProfile!
processor.state = ProfileSwitcherState(
accounts: alternateAccounts + [current],
activeAccountId: current.userId,
allowLockAndLogout: true,
isVisible: true,
)
let secondAlternateRow = try subject.inspect().find(button: "")
try secondAlternateRow.labelView().callOnTapGesture()
waitFor(!processor.effects.isEmpty)
XCTAssertEqual(processor.effects.last, .accountPressed(secondAlternate))
}
/// Tapping the background triggers a `.backgroundPressed` action.
@MainActor
func test_background_tap() throws {
let view = try subject.inspect().view(ProfileSwitcherView.self)
let background = view.first
try background?.callOnTapGesture()
XCTAssertEqual(processor.dispatchedActions.last, .backgroundTapped)
}
/// Tests the add account visibility below the maximum account limit
@MainActor
func test_addAccountRow_subMaximumAccounts_showAdd() throws {
@ -234,131 +83,4 @@ class ProfileSwitcherViewTests: BitwardenTestCase { // swiftlint:disable:this ty
processor.state = ProfileSwitcherState.maximumAccounts
XCTAssertFalse(subject.store.state.showsAddAccount)
}
// MARK: Snapshots
func disabletest_snapshot_singleAccount() {
assertSnapshot(of: subject, as: .defaultPortrait)
}
@MainActor
func disabletest_snapshot_multiAccount_unlocked_belowMaximum() {
processor.state = ProfileSwitcherState(
accounts: [
ProfileSwitcherItem.anneAccount,
ProfileSwitcherItem.fixture(
color: .yellow,
email: "bonus.bridge@bitwarden.com",
isUnlocked: true,
userInitials: "BB",
),
ProfileSwitcherItem.fixture(
color: .teal,
email: "concurrent.claim@bitarden.com",
isUnlocked: true,
userInitials: "CC",
),
ProfileSwitcherItem.fixture(
color: .indigo,
email: "double.dip@bitwarde.com",
isUnlocked: true,
userInitials: "DD",
),
],
activeAccountId: ProfileSwitcherItem.anneAccount.userId,
allowLockAndLogout: true,
isVisible: true,
)
assertSnapshot(of: subject, as: .defaultPortrait)
}
@MainActor
func disabletest_snapshot_multiAccount_unlocked_atMaximum() {
processor.state = ProfileSwitcherState.maximumAccounts
assertSnapshot(of: subject, as: .defaultPortrait)
}
@MainActor
func disabletest_snapshot_multiAccount_unlocked_atMaximum_largeText() {
processor.state = ProfileSwitcherState.maximumAccounts
assertSnapshot(of: subject, as: .defaultPortraitAX5)
}
@MainActor
func disabletest_snapshot_multiAccount_locked_belowMaximum() {
processor.state = ProfileSwitcherState(
accounts: [
ProfileSwitcherItem.fixture(
color: .yellow,
email: "bonus.bridge@bitwarden.com",
isUnlocked: false,
userInitials: "BB",
),
ProfileSwitcherItem.fixture(
color: .teal,
email: "concurrent.claim@bitarden.com",
isUnlocked: false,
userInitials: "CC",
),
ProfileSwitcherItem.anneAccount,
ProfileSwitcherItem.fixture(
color: .indigo,
email: "double.dip@bitwarde.com",
isUnlocked: false,
userInitials: "DD",
),
],
activeAccountId: ProfileSwitcherItem.anneAccount.userId,
allowLockAndLogout: true,
isVisible: true,
)
assertSnapshot(of: subject, as: .defaultPortrait)
}
@MainActor
func disabletest_snapshot_multiAccount_locked_atMaximum() {
processor.state = ProfileSwitcherState(
accounts: [
ProfileSwitcherItem.fixture(
color: .yellow,
email: "bonus.bridge@bitwarden.com",
isUnlocked: false,
userInitials: "BB",
),
ProfileSwitcherItem.fixture(
color: .teal,
email: "concurrent.claim@bitarden.com",
isUnlocked: false,
userInitials: "CC",
),
.anneAccount,
ProfileSwitcherItem.fixture(
color: .indigo,
email: "double.dip@bitwarde.com",
isUnlocked: false,
userInitials: "DD",
),
ProfileSwitcherItem.fixture(
color: .green,
email: "extra.edition@bitwarden.com",
isUnlocked: false,
userInitials: "EE",
),
],
activeAccountId: ProfileSwitcherItem.anneAccount.userId,
allowLockAndLogout: true,
isVisible: true,
)
assertSnapshot(of: subject, as: .defaultPortrait)
}
/// Test a snapshot of the ProfileSwitcherView previews.
func disabletest_snapshot_profileSwitcherView_previews() {
for preview in ProfileSwitcherView_Previews._allPreviews {
assertSnapshots(
of: preview.content,
as: [.defaultPortrait],
)
}
}
}

View File

@ -0,0 +1,46 @@
// swiftlint:disable:this file_name
import BitwardenResources
import SnapshotTesting
import XCTest
@testable import BitwardenShared
class RemoveMasterPasswordViewTests: BitwardenTestCase {
// MARK: Properties
var processor: MockProcessor<RemoveMasterPasswordState, RemoveMasterPasswordAction, RemoveMasterPasswordEffect>!
var subject: RemoveMasterPasswordView!
// MARK: Setup & Teardown
override func setUp() {
super.setUp()
processor = MockProcessor(state: RemoveMasterPasswordState(
masterPassword: "password",
organizationName: "Example Org",
organizationId: "ORG_ID",
keyConnectorUrl: "https://example.com",
))
subject = RemoveMasterPasswordView(store: Store(processor: processor))
}
override func tearDown() {
super.tearDown()
processor = nil
subject = nil
}
// MARK: Snapshots
/// The remove master password view renders correctly.
@MainActor
func disabletest_snapshot_removeMasterPassword() {
assertSnapshots(
of: subject.navStackWrapped,
as: [.defaultPortrait, .defaultPortraitDark, .tallPortraitAX5(heightMultiple: 1.5)],
)
}
}

View File

@ -1,5 +1,5 @@
// swiftlint:disable:this file_name
import BitwardenResources
import SnapshotTesting
import ViewInspector
import XCTest
@ -42,15 +42,4 @@ class RemoveMasterPasswordViewTests: BitwardenTestCase {
try await button.tap()
XCTAssertEqual(processor.effects.last, .continueFlow)
}
// MARK: Snapshots
/// The remove master password view renders correctly.
@MainActor
func disabletest_snapshot_removeMasterPassword() {
assertSnapshots(
of: subject.navStackWrapped,
as: [.defaultPortrait, .defaultPortraitDark, .tallPortraitAX5(heightMultiple: 1.5)],
)
}
}

View File

@ -0,0 +1,66 @@
// swiftlint:disable:this file_name
import BitwardenResources
import SnapshotTesting
import XCTest
@testable import BitwardenShared
class SetMasterPasswordViewTests: BitwardenTestCase {
// MARK: Properties
var processor: MockProcessor<SetMasterPasswordState, SetMasterPasswordAction, SetMasterPasswordEffect>!
var subject: SetMasterPasswordView!
// MARK: Setup & Teardown
override func setUp() {
super.setUp()
processor = MockProcessor(state: SetMasterPasswordState(organizationIdentifier: "ORG_ID"))
subject = SetMasterPasswordView(store: Store(processor: processor))
}
override func tearDown() {
super.tearDown()
processor = nil
subject = nil
}
// MARK: Snapshots
/// A snapshot of the view with all filled values fields.
@MainActor
func disabletest_snapshot_setPassword_filled() {
processor.state.masterPassword = "password123"
processor.state.masterPasswordRetype = "password123"
processor.state.masterPasswordHint = "hint hint"
processor.state.resetPasswordAutoEnroll = true
assertSnapshots(
of: subject.navStackWrapped,
as: [
"portrait": .portrait(),
"portraitDark": .portraitDark(),
"tallPortraitAX5": .tallPortraitAX5(),
],
)
}
/// A snapshot of the view for privilege elevation.
@MainActor
func disabletest_snapshot_setPassword_privilege_elevation() {
processor.state.isPrivilegeElevation = true
processor.state.masterPassword = "password123"
processor.state.masterPasswordRetype = "password123"
processor.state.masterPasswordHint = "hint hint"
processor.state.resetPasswordAutoEnroll = true
assertSnapshots(
of: subject.navStackWrapped,
as: [
"portrait": .portrait(),
"portraitDark": .portraitDark(),
"tallPortraitAX5": .tallPortraitAX5(),
],
)
}
}

View File

@ -1,5 +1,5 @@
// swiftlint:disable:this file_name
import BitwardenResources
import SnapshotTesting
import ViewInspector
import XCTest
@ -99,41 +99,4 @@ class SetMasterPasswordViewTests: BitwardenTestCase {
waitFor(!processor.effects.isEmpty)
XCTAssertEqual(processor.effects.last, .saveTapped)
}
// MARK: Snapshots
/// A snapshot of the view with all filled values fields.
@MainActor
func disabletest_snapshot_setPassword_filled() {
processor.state.masterPassword = "password123"
processor.state.masterPasswordRetype = "password123"
processor.state.masterPasswordHint = "hint hint"
processor.state.resetPasswordAutoEnroll = true
assertSnapshots(
of: subject.navStackWrapped,
as: [
"portrait": .portrait(),
"portraitDark": .portraitDark(),
"tallPortraitAX5": .tallPortraitAX5(),
],
)
}
/// A snapshot of the view for privilege elevation.
@MainActor
func disabletest_snapshot_setPassword_privilege_elevation() {
processor.state.isPrivilegeElevation = true
processor.state.masterPassword = "password123"
processor.state.masterPasswordRetype = "password123"
processor.state.masterPasswordHint = "hint hint"
processor.state.resetPasswordAutoEnroll = true
assertSnapshots(
of: subject.navStackWrapped,
as: [
"portrait": .portrait(),
"portraitDark": .portraitDark(),
"tallPortraitAX5": .tallPortraitAX5(),
],
)
}
}

View File

@ -0,0 +1,45 @@
// swiftlint:disable:this file_name
import BitwardenResources
import SnapshotTesting
import SwiftUI
import XCTest
@testable import BitwardenShared
// MARK: - CheckEmailViewTests
class CheckEmailViewTests: BitwardenTestCase {
// MARK: Properties
var processor: MockProcessor<CheckEmailState, CheckEmailAction, Void>!
var subject: CheckEmailView!
// MARK: Setup & Teardown
override func setUp() {
super.setUp()
processor = MockProcessor(state: CheckEmailState(email: "example@email.com"))
let store = Store(processor: processor)
subject = CheckEmailView(store: store)
}
override func tearDown() {
super.tearDown()
processor = nil
subject = nil
}
// MARK: Snapshots
/// Tests the view renders correctly.
func disabletest_snapshot_empty() {
assertSnapshots(
of: subject,
as: [
.defaultPortrait,
.defaultPortraitDark,
.tallPortraitAX5(heightMultiple: 2),
],
)
}
}

View File

@ -1,5 +1,5 @@
// swiftlint:disable:this file_name
import BitwardenResources
import SnapshotTesting
import SwiftUI
import ViewInspector
import XCTest
@ -46,18 +46,4 @@ class CheckEmailViewTests: BitwardenTestCase {
try button.tap()
XCTAssertEqual(processor.dispatchedActions.last, .goBackTapped)
}
// MARK: Snapshots
/// Tests the view renders correctly.
func disabletest_snapshot_empty() {
assertSnapshots(
of: subject,
as: [
.defaultPortrait,
.defaultPortraitDark,
.tallPortraitAX5(heightMultiple: 2),
],
)
}
}

View File

@ -0,0 +1,73 @@
// swiftlint:disable:this file_name
import BitwardenResources
import SnapshotTesting
import SwiftUI
import XCTest
@testable import BitwardenShared
// MARK: - StartRegistrationViewTests
class StartRegistrationViewTests: BitwardenTestCase {
// MARK: Properties
var processor: MockProcessor<StartRegistrationState, StartRegistrationAction, StartRegistrationEffect>!
var subject: StartRegistrationView!
// MARK: Setup & Teardown
override func setUp() {
super.setUp()
processor = MockProcessor(state: StartRegistrationState())
let store = Store(processor: processor)
subject = StartRegistrationView(store: store)
}
override func tearDown() {
super.tearDown()
processor = nil
subject = nil
}
// MARK: Snapshots
/// Tests the view renders correctly when the text fields are all empty.
@MainActor
func disabletest_snapshot_empty() {
assertSnapshots(of: subject, as: [.defaultPortrait, .defaultPortraitDark])
}
/// Tests the view renders correctly when the text fields are all populated.
@MainActor
func disabletest_snapshot_textFields_populated() throws {
processor.state.emailText = "email@example.com"
processor.state.nameText = "user name"
assertSnapshots(of: subject, as: [.defaultPortrait, .defaultPortraitDark, .defaultPortraitAX5])
}
/// Tests the view renders correctly when the text fields are all populated with long text.
@MainActor
func disabletest_snapshot_textFields_populated_long() throws {
processor.state.emailText = "emailmmmmmmmmmmmmmmmmmmmmm@exammmmmmmmmmmmmmmmmmmmmmmmmmmmmmmple.com"
processor.state.nameText = "user name name name name name name name name name name name name name name"
assertSnapshots(of: subject, as: [.defaultPortrait, .defaultPortraitDark, .defaultPortraitAX5])
}
/// Tests the view renders correctly when the toggles are on.
@MainActor
func disabletest_snapshot_toggles_on() throws {
processor.state.isReceiveMarketingToggleOn = true
assertSnapshot(of: subject, as: .defaultPortrait)
}
/// Tests the view renders correctly when the marketing toggle is hidden.
@MainActor
func disabletest_snapshot_marketingToggle_hidden() throws {
processor.state.showReceiveMarketingToggle = false
assertSnapshot(of: subject, as: .defaultPortrait)
}
}

View File

@ -1,5 +1,5 @@
// swiftlint:disable:this file_name
import BitwardenResources
import SnapshotTesting
import SwiftUI
import ViewInspector
import XCTest
@ -87,46 +87,4 @@ class StartRegistrationViewTests: BitwardenTestCase {
try toggle.tap()
XCTAssertEqual(processor.dispatchedActions.last, .toggleReceiveMarketing(true))
}
// MARK: Snapshots
/// Tests the view renders correctly when the text fields are all empty.
@MainActor
func disabletest_snapshot_empty() {
assertSnapshots(of: subject, as: [.defaultPortrait, .defaultPortraitDark])
}
/// Tests the view renders correctly when the text fields are all populated.
@MainActor
func disabletest_snapshot_textFields_populated() throws {
processor.state.emailText = "email@example.com"
processor.state.nameText = "user name"
assertSnapshots(of: subject, as: [.defaultPortrait, .defaultPortraitDark, .defaultPortraitAX5])
}
/// Tests the view renders correctly when the text fields are all populated with long text.
@MainActor
func disabletest_snapshot_textFields_populated_long() throws {
processor.state.emailText = "emailmmmmmmmmmmmmmmmmmmmmm@exammmmmmmmmmmmmmmmmmmmmmmmmmmmmmmple.com"
processor.state.nameText = "user name name name name name name name name name name name name name name"
assertSnapshots(of: subject, as: [.defaultPortrait, .defaultPortraitDark, .defaultPortraitAX5])
}
/// Tests the view renders correctly when the toggles are on.
@MainActor
func disabletest_snapshot_toggles_on() throws {
processor.state.isReceiveMarketingToggleOn = true
assertSnapshot(of: subject, as: .defaultPortrait)
}
/// Tests the view renders correctly when the marketing toggle is hidden.
@MainActor
func disabletest_snapshot_marketingToggle_hidden() throws {
processor.state.showReceiveMarketingToggle = false
assertSnapshot(of: subject, as: .defaultPortrait)
}
}

View File

@ -0,0 +1,106 @@
// swiftlint:disable:this file_name
import BitwardenResources
import SnapshotTesting
import XCTest
@testable import BitwardenShared
// MARK: - UpdateMasterPasswordViewTests
class UpdateMasterPasswordViewTests: BitwardenTestCase {
// MARK: Properties
var processor: MockProcessor<UpdateMasterPasswordState, UpdateMasterPasswordAction, UpdateMasterPasswordEffect>!
var subject: UpdateMasterPasswordView!
// MARK: Setup & Teardown
override func setUp() {
super.setUp()
let state = UpdateMasterPasswordState(
currentMasterPassword: "current master password",
masterPassword: "new master password",
masterPasswordHint: "new master password hint",
masterPasswordPolicy: .init(
minComplexity: 0,
minLength: 20,
requireUpper: true,
requireLower: false,
requireNumbers: false,
requireSpecial: false,
enforceOnLogin: true,
),
masterPasswordRetype: "new master password",
)
processor = MockProcessor(state: state)
subject = UpdateMasterPasswordView(store: Store(processor: processor))
}
override func tearDown() {
super.tearDown()
processor = nil
subject = nil
}
// MARK: Snapshots
/// A snapshot of the view with all filled values fields.
@MainActor
func disabletest_snapshot_resetPassword_withFilled_default() {
processor.state.forcePasswordResetReason = .adminForcePasswordReset
assertSnapshots(
of: subject.navStackWrapped,
as: [.portrait(heightMultiple: 1.25)],
)
}
/// A snapshot of the view with all filled values fields in a dark mode.
@MainActor
func disabletest_snapshot_resetPassword_withFilled_dark() {
processor.state.forcePasswordResetReason = .adminForcePasswordReset
assertSnapshots(
of: subject.navStackWrapped,
as: [.portraitDark(heightMultiple: 1.25)],
)
}
/// A snapshot of the view with all filled values fields in a large text.
@MainActor
func disabletest_snapshot_resetPassword_withFilled_large() {
processor.state.forcePasswordResetReason = .adminForcePasswordReset
assertSnapshots(
of: subject.navStackWrapped,
as: [.tallPortraitAX5(heightMultiple: 6)],
)
}
/// A snapshot of the view with all filled values fields.
@MainActor
func disabletest_snapshot_weakPassword_withFilled_default() {
processor.state.forcePasswordResetReason = .weakMasterPasswordOnLogin
assertSnapshots(
of: subject.navStackWrapped,
as: [.portrait(heightMultiple: 1.25)],
)
}
/// A snapshot of the view with all filled values fields in a dark mode.
@MainActor
func disabletest_snapshot_weakPassword_withFilled_dark() {
processor.state.forcePasswordResetReason = .weakMasterPasswordOnLogin
assertSnapshots(
of: subject.navStackWrapped,
as: [.portraitDark(heightMultiple: 1.25)],
)
}
/// A snapshot of the view with all filled values fields in a large text.
@MainActor
func disabletest_snapshot_weakPassword_withFilled_large() {
processor.state.forcePasswordResetReason = .weakMasterPasswordOnLogin
assertSnapshots(
of: subject.navStackWrapped,
as: [.tallPortraitAX5(heightMultiple: 6)],
)
}
}

View File

@ -1,5 +1,5 @@
// swiftlint:disable:this file_name
import BitwardenResources
import SnapshotTesting
import ViewInspector
import XCTest
@ -145,66 +145,4 @@ class UpdateMasterPasswordViewTests: BitwardenTestCase {
try await button.tap()
XCTAssertEqual(processor.effects.last, .saveTapped)
}
// MARK: Snapshots
/// A snapshot of the view with all filled values fields.
@MainActor
func disabletest_snapshot_resetPassword_withFilled_default() {
processor.state.forcePasswordResetReason = .adminForcePasswordReset
assertSnapshots(
of: subject.navStackWrapped,
as: [.portrait(heightMultiple: 1.25)],
)
}
/// A snapshot of the view with all filled values fields in a dark mode.
@MainActor
func disabletest_snapshot_resetPassword_withFilled_dark() {
processor.state.forcePasswordResetReason = .adminForcePasswordReset
assertSnapshots(
of: subject.navStackWrapped,
as: [.portraitDark(heightMultiple: 1.25)],
)
}
/// A snapshot of the view with all filled values fields in a large text.
@MainActor
func disabletest_snapshot_resetPassword_withFilled_large() {
processor.state.forcePasswordResetReason = .adminForcePasswordReset
assertSnapshots(
of: subject.navStackWrapped,
as: [.tallPortraitAX5(heightMultiple: 6)],
)
}
/// A snapshot of the view with all filled values fields.
@MainActor
func disabletest_snapshot_weakPassword_withFilled_default() {
processor.state.forcePasswordResetReason = .weakMasterPasswordOnLogin
assertSnapshots(
of: subject.navStackWrapped,
as: [.portrait(heightMultiple: 1.25)],
)
}
/// A snapshot of the view with all filled values fields in a dark mode.
@MainActor
func disabletest_snapshot_weakPassword_withFilled_dark() {
processor.state.forcePasswordResetReason = .weakMasterPasswordOnLogin
assertSnapshots(
of: subject.navStackWrapped,
as: [.portraitDark(heightMultiple: 1.25)],
)
}
/// A snapshot of the view with all filled values fields in a large text.
@MainActor
func disabletest_snapshot_weakPassword_withFilled_large() {
processor.state.forcePasswordResetReason = .weakMasterPasswordOnLogin
assertSnapshots(
of: subject.navStackWrapped,
as: [.tallPortraitAX5(heightMultiple: 6)],
)
}
}

View File

@ -0,0 +1,26 @@
import Foundation
import XCTest
@testable import BitwardenShared
class MockUserVerificationHelperDelegate: UserVerificationDelegate {
var alertShown = [Alert]()
var alertShownHandler: ((Alert) async throws -> Void)?
var alertOnDismissed: (() -> Void)?
func showAlert(_ alert: Alert) {
alertShown.append(alert)
Task {
do {
try await alertShownHandler?(alert)
} catch {
XCTFail("Error calling alert shown handler: \(error)")
}
}
}
func showAlert(_ alert: BitwardenShared.Alert, onDismissed: (() -> Void)?) {
showAlert(alert)
alertOnDismissed = onDismissed
}
}

View File

@ -425,26 +425,4 @@ class UserVerificationHelperTests: BitwardenTestCase { // swiftlint:disable:this
try alert.setText("pin", forTextFieldWithId: "pin")
try await alert.tapAction(title: Localizations.submit)
}
}
class MockUserVerificationHelperDelegate: UserVerificationDelegate {
var alertShown = [Alert]()
var alertShownHandler: ((Alert) async throws -> Void)?
var alertOnDismissed: (() -> Void)?
func showAlert(_ alert: Alert) {
alertShown.append(alert)
Task {
do {
try await alertShownHandler?(alert)
} catch {
XCTFail("Error calling alert shown handler: \(error)")
}
}
}
func showAlert(_ alert: BitwardenShared.Alert, onDismissed: (() -> Void)?) {
showAlert(alert)
alertOnDismissed = onDismissed
}
} // swiftlint:disable:this file_length

View File

@ -1,3 +1,4 @@
// swiftlint:disable:this file_name
import BitwardenResources
import SnapshotTesting
import XCTest
@ -40,104 +41,6 @@ class VaultUnlockViewTests: BitwardenTestCase {
subject = nil
}
// MARK: Tests
/// Tapping the cancel button in the navigation bar dispatches the `.cancelPressed` action.
@MainActor
func test_cancelButton_tap() throws {
processor.state.isInAppExtension = true
let button = try subject.inspect().findCancelToolbarButton()
try button.tap()
XCTAssertEqual(processor.dispatchedActions.last, .cancelPressed)
}
/// The secure field is visible when `isMasterPasswordRevealed` is `false`.
@MainActor
func test_isMasterPasswordRevealed_false() throws {
processor.state.isMasterPasswordRevealed = false
XCTAssertNoThrow(try subject.inspect().find(secureField: ""))
let textField = try subject.inspect().find(textField: "")
XCTAssertTrue(textField.isHidden())
}
/// The text field is visible when `isMasterPasswordRevealed` is `true`.
@MainActor
func test_isMasterPasswordRevealed_true() throws {
processor.state.isMasterPasswordRevealed = true
XCTAssertNoThrow(try subject.inspect().find(textField: ""))
XCTAssertThrowsError(try subject.inspect().find(secureField: ""))
}
/// Tapping the options button in the navigation bar dispatches the `.morePressed` action.
@MainActor
func test_optionsButton_logOut_tap() throws {
let button = try subject.inspect().find(button: Localizations.logOut)
try button.tap()
XCTAssertEqual(processor.dispatchedActions.last, .logOut)
}
/// Updating the secure field dispatches the `.masterPasswordChanged()` action.
@MainActor
func test_secureField_updateValue() throws {
processor.state.isMasterPasswordRevealed = false
let secureField = try subject.inspect().find(secureField: "")
try secureField.setInput("text")
XCTAssertEqual(processor.dispatchedActions.last, .masterPasswordChanged("text"))
}
/// Updating the text field dispatches the `.masterPasswordChanged()` action.
@MainActor
func test_textField_updateValue() throws {
processor.state.isMasterPasswordRevealed = true
let textField = try subject.inspect().find(textField: "")
try textField.setInput("text")
XCTAssertEqual(processor.dispatchedActions.last, .masterPasswordChanged("text"))
}
/// Tapping the vault unlock button dispatches the `.unlockVault` action.
@MainActor
func test_vaultUnlockButton_tap() async throws {
let button = try subject.inspect().find(asyncButton: Localizations.unlock)
try await button.tap()
waitFor(!processor.effects.isEmpty)
XCTAssertEqual(processor.effects.last, .unlockVault)
}
/// Tapping the vault biometric unlock button dispatches the `.unlockVaultWithBiometrics` action.
@MainActor
func test_vaultUnlockWithBiometricsButton_tap() throws {
processor.state.biometricUnlockStatus = .available(
.faceID,
enabled: true,
)
var expectedString = Localizations.useFaceIDToUnlock
var button = try subject.inspect().find(button: expectedString)
processor.state.biometricUnlockStatus = .available(
.touchID,
enabled: true,
)
expectedString = Localizations.useFingerprintToUnlock
button = try subject.inspect().find(button: expectedString)
processor.state.biometricUnlockStatus = .available(
.opticID,
enabled: true,
)
expectedString = Localizations.useOpticIDToUnlock
button = try subject.inspect().find(button: expectedString)
processor.state.biometricUnlockStatus = .available(
.unknown,
enabled: true,
)
expectedString = Localizations.useBiometricsToUnlock
button = try subject.inspect().find(button: expectedString)
try button.tap()
waitFor(!processor.effects.isEmpty)
XCTAssertEqual(processor.effects.last, .unlockVaultWithBiometrics)
}
// MARK: Snapshots
/// Test a snapshot of the empty view.

View File

@ -0,0 +1,140 @@
// swiftlint:disable:this file_name
import BitwardenResources
import XCTest
@testable import BitwardenShared
class VaultUnlockViewTests: BitwardenTestCase {
// MARK: Properties
var processor: MockProcessor<VaultUnlockState, VaultUnlockAction, VaultUnlockEffect>!
var subject: VaultUnlockView!
// MARK: Setup & Teardown
override func setUp() {
super.setUp()
processor = MockProcessor(
state: VaultUnlockState(
email: "user@bitwarden.com",
profileSwitcherState: .init(
accounts: [],
activeAccountId: nil,
allowLockAndLogout: false,
isVisible: false,
),
unlockMethod: .password,
webVaultHost: "bitwarden.com",
),
)
let store = Store(processor: processor)
subject = VaultUnlockView(store: store)
}
override func tearDown() {
super.tearDown()
processor = nil
subject = nil
}
// MARK: Tests
/// Tapping the cancel button in the navigation bar dispatches the `.cancelPressed` action.
@MainActor
func test_cancelButton_tap() throws {
processor.state.isInAppExtension = true
let button = try subject.inspect().findCancelToolbarButton()
try button.tap()
XCTAssertEqual(processor.dispatchedActions.last, .cancelPressed)
}
/// The secure field is visible when `isMasterPasswordRevealed` is `false`.
@MainActor
func test_isMasterPasswordRevealed_false() throws {
processor.state.isMasterPasswordRevealed = false
XCTAssertNoThrow(try subject.inspect().find(secureField: ""))
let textField = try subject.inspect().find(textField: "")
XCTAssertTrue(textField.isHidden())
}
/// The text field is visible when `isMasterPasswordRevealed` is `true`.
@MainActor
func test_isMasterPasswordRevealed_true() throws {
processor.state.isMasterPasswordRevealed = true
XCTAssertNoThrow(try subject.inspect().find(textField: ""))
XCTAssertThrowsError(try subject.inspect().find(secureField: ""))
}
/// Tapping the options button in the navigation bar dispatches the `.morePressed` action.
@MainActor
func test_optionsButton_logOut_tap() throws {
let button = try subject.inspect().find(button: Localizations.logOut)
try button.tap()
XCTAssertEqual(processor.dispatchedActions.last, .logOut)
}
/// Updating the secure field dispatches the `.masterPasswordChanged()` action.
@MainActor
func test_secureField_updateValue() throws {
processor.state.isMasterPasswordRevealed = false
let secureField = try subject.inspect().find(secureField: "")
try secureField.setInput("text")
XCTAssertEqual(processor.dispatchedActions.last, .masterPasswordChanged("text"))
}
/// Updating the text field dispatches the `.masterPasswordChanged()` action.
@MainActor
func test_textField_updateValue() throws {
processor.state.isMasterPasswordRevealed = true
let textField = try subject.inspect().find(textField: "")
try textField.setInput("text")
XCTAssertEqual(processor.dispatchedActions.last, .masterPasswordChanged("text"))
}
/// Tapping the vault unlock button dispatches the `.unlockVault` action.
@MainActor
func test_vaultUnlockButton_tap() async throws {
let button = try subject.inspect().find(asyncButton: Localizations.unlock)
try await button.tap()
waitFor(!processor.effects.isEmpty)
XCTAssertEqual(processor.effects.last, .unlockVault)
}
/// Tapping the vault biometric unlock button dispatches the `.unlockVaultWithBiometrics` action.
@MainActor
func test_vaultUnlockWithBiometricsButton_tap() throws {
processor.state.biometricUnlockStatus = .available(
.faceID,
enabled: true,
)
var expectedString = Localizations.useFaceIDToUnlock
var button = try subject.inspect().find(button: expectedString)
processor.state.biometricUnlockStatus = .available(
.touchID,
enabled: true,
)
expectedString = Localizations.useFingerprintToUnlock
button = try subject.inspect().find(button: expectedString)
processor.state.biometricUnlockStatus = .available(
.opticID,
enabled: true,
)
expectedString = Localizations.useOpticIDToUnlock
button = try subject.inspect().find(button: expectedString)
processor.state.biometricUnlockStatus = .available(
.unknown,
enabled: true,
)
expectedString = Localizations.useBiometricsToUnlock
button = try subject.inspect().find(button: expectedString)
try button.tap()
waitFor(!processor.effects.isEmpty)
XCTAssertEqual(processor.effects.last, .unlockVaultWithBiometrics)
}
}

View File

@ -0,0 +1,82 @@
// swiftlint:disable:this file_name
import BitwardenResources
import SnapshotTesting
import XCTest
@testable import BitwardenShared
class VaultUnlockSetupViewTests: BitwardenTestCase {
// MARK: Properties
var processor: MockProcessor<VaultUnlockSetupState, VaultUnlockSetupAction, VaultUnlockSetupEffect>!
var subject: VaultUnlockSetupView!
// MARK: Setup & Teardown
override func setUp() {
super.setUp()
processor = MockProcessor(state: VaultUnlockSetupState(accountSetupFlow: .createAccount))
subject = VaultUnlockSetupView(store: Store(processor: processor))
}
override func tearDown() {
super.tearDown()
processor = nil
subject = nil
}
// MARK: Snapshots
/// The vault unlock setup view renders correctly.
@MainActor
func disabletest_snapshot_vaultUnlockSetup() {
processor.state.biometricsStatus = .available(.faceID, enabled: false)
assertSnapshots(
of: subject.navStackWrapped,
as: [.defaultPortrait, .defaultPortraitDark, .tallPortraitAX5(heightMultiple: 2), .defaultLandscape],
)
}
/// The vault unlock setup view renders correctly when shown from settings.
@MainActor
func disabletest_snapshot_vaultUnlockSetup_settings() {
processor.state.accountSetupFlow = .settings
processor.state.biometricsStatus = .available(.faceID, enabled: false)
assertSnapshots(
of: subject.navStackWrapped,
as: [.defaultPortrait, .defaultPortraitDark, .tallPortraitAX5(heightMultiple: 2)],
)
}
/// The vault unlock setup view renders correctly for a device with Touch ID.
@MainActor
func disabletest_snapshot_vaultUnlockSetup_touchID() {
processor.state.biometricsStatus = .available(.touchID, enabled: false)
assertSnapshots(
of: subject.navStackWrapped,
as: [.defaultPortrait],
)
}
/// The vault unlock setup view renders correctly for a device without biometrics.
@MainActor
func disabletest_snapshot_vaultUnlockSetup_noBiometrics() {
assertSnapshots(
of: subject.navStackWrapped,
as: [.defaultPortrait],
)
}
/// The vault unlock setup view renders correctly with an unlock method enabled.
@MainActor
func disabletest_snapshot_vaultUnlockSetup_unlockMethodEnabled() {
processor.state.biometricsStatus = .available(.faceID, enabled: true)
assertSnapshots(
of: subject.navStackWrapped,
as: [.defaultPortrait],
)
}
}

View File

@ -1,5 +1,5 @@
// swiftlint:disable:this file_name
import BitwardenResources
import SnapshotTesting
import ViewInspector
import XCTest
@ -78,56 +78,4 @@ class VaultUnlockSetupViewTests: BitwardenTestCase {
try button.tap()
XCTAssertEqual(processor.dispatchedActions.last, .setUpLater)
}
// MARK: Snapshots
/// The vault unlock setup view renders correctly.
@MainActor
func disabletest_snapshot_vaultUnlockSetup() {
processor.state.biometricsStatus = .available(.faceID, enabled: false)
assertSnapshots(
of: subject.navStackWrapped,
as: [.defaultPortrait, .defaultPortraitDark, .tallPortraitAX5(heightMultiple: 2), .defaultLandscape],
)
}
/// The vault unlock setup view renders correctly when shown from settings.
@MainActor
func disabletest_snapshot_vaultUnlockSetup_settings() {
processor.state.accountSetupFlow = .settings
processor.state.biometricsStatus = .available(.faceID, enabled: false)
assertSnapshots(
of: subject.navStackWrapped,
as: [.defaultPortrait, .defaultPortraitDark, .tallPortraitAX5(heightMultiple: 2)],
)
}
/// The vault unlock setup view renders correctly for a device with Touch ID.
@MainActor
func disabletest_snapshot_vaultUnlockSetup_touchID() {
processor.state.biometricsStatus = .available(.touchID, enabled: false)
assertSnapshots(
of: subject.navStackWrapped,
as: [.defaultPortrait],
)
}
/// The vault unlock setup view renders correctly for a device without biometrics.
@MainActor
func disabletest_snapshot_vaultUnlockSetup_noBiometrics() {
assertSnapshots(
of: subject.navStackWrapped,
as: [.defaultPortrait],
)
}
/// The vault unlock setup view renders correctly with an unlock method enabled.
@MainActor
func disabletest_snapshot_vaultUnlockSetup_unlockMethodEnabled() {
processor.state.biometricsStatus = .available(.faceID, enabled: true)
assertSnapshots(
of: subject.navStackWrapped,
as: [.defaultPortrait],
)
}
}

View File

@ -419,18 +419,4 @@ class Fido2UserVerificationMediatorTests: BitwardenTestCase { // swiftlint:disab
}
}
}
}
class MockFido2UserVerificationMediatorDelegate:
MockUserVerificationHelperDelegate,
Fido2UserVerificationMediatorDelegate {
var onNeedsUserInteractionCalled = false
var onNeedsUserInteractionError: Error?
func onNeedsUserInteraction() async throws {
onNeedsUserInteractionCalled = true
if let onNeedsUserInteractionError {
throw onNeedsUserInteractionError
}
}
} // swiftlint:disable:this file_length

View File

@ -0,0 +1,17 @@
import BitwardenSdk
@testable import BitwardenShared
class MockFido2UserVerificationMediatorDelegate:
MockUserVerificationHelperDelegate,
Fido2UserVerificationMediatorDelegate {
var onNeedsUserInteractionCalled = false
var onNeedsUserInteractionError: Error?
func onNeedsUserInteraction() async throws {
onNeedsUserInteractionCalled = true
if let onNeedsUserInteractionError {
throw onNeedsUserInteractionError
}
}
}

View File

@ -1,3 +1,4 @@
// swiftlint:disable:this file_name
import BitwardenKit
import SwiftUI
import ViewInspector

View File

@ -1,3 +1,4 @@
// swiftlint:disable:this file_name
import BitwardenKitMocks
import SnapshotTesting
import XCTest

View File

@ -1,3 +1,4 @@
// swiftlint:disable:this file_name
import SwiftUI
import ViewInspector
import XCTest

Some files were not shown because too many files have changed in this diff Show More