mirror of
https://github.com/bitwarden/ios.git
synced 2025-12-10 00:42:29 -06:00
[PM-14425] [BEEEP] Add test plans (#1106)
Co-authored-by: Katherine Bertelsen <kbertelsen@bitwarden.com>
This commit is contained in:
parent
11128e13f6
commit
07176cd3c0
@ -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
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
// swiftlint:disable:this file_name
|
||||
import SnapshotTesting
|
||||
import SwiftUI
|
||||
import ViewInspector
|
||||
import XCTest
|
||||
|
||||
@testable import AuthenticatorShared
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
@ -1,3 +1,4 @@
|
||||
// swiftlint:disable:this file_name
|
||||
import SnapshotTesting
|
||||
import XCTest
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
// swiftlint:disable:this file_name
|
||||
import SnapshotTesting
|
||||
import XCTest
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
// swiftlint:disable:this file_name
|
||||
import SnapshotTesting
|
||||
import XCTest
|
||||
|
||||
@ -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])
|
||||
}
|
||||
}
|
||||
@ -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])
|
||||
}
|
||||
}
|
||||
@ -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])
|
||||
}
|
||||
}
|
||||
@ -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])
|
||||
}
|
||||
}
|
||||
@ -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()],
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -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()],
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -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],
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -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],
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -1,6 +1,6 @@
|
||||
// swiftlint:disable:this file_name
|
||||
import SnapshotTesting
|
||||
import SwiftUI
|
||||
import ViewInspector
|
||||
import XCTest
|
||||
|
||||
@testable import AuthenticatorShared
|
||||
@ -1,6 +1,6 @@
|
||||
// swiftlint:disable:this file_name
|
||||
import SnapshotTesting
|
||||
import SwiftUI
|
||||
import ViewInspector
|
||||
import XCTest
|
||||
|
||||
@testable import AuthenticatorShared
|
||||
@ -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),
|
||||
],
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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(
|
||||
@ -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))
|
||||
}
|
||||
}
|
||||
@ -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))
|
||||
}
|
||||
}
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
@ -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 {
|
||||
|
||||
@ -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,
|
||||
],
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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,
|
||||
],
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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.
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
@ -1,6 +0,0 @@
|
||||
import SwiftUI
|
||||
import XCTest
|
||||
|
||||
@testable import BitwardenActionExtension
|
||||
|
||||
class ActionViewControllerTests: BitwardenTestCase {}
|
||||
@ -1,6 +0,0 @@
|
||||
import SwiftUI
|
||||
import XCTest
|
||||
|
||||
@testable import BitwardenAutoFillExtension
|
||||
|
||||
class CredentialProviderViewControllerTests: BitwardenTestCase {}
|
||||
@ -1,3 +1,4 @@
|
||||
// swiftlint:disable:this file_name
|
||||
import SnapshotTesting
|
||||
import XCTest
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
// swiftlint:disable:this file_name
|
||||
import BitwardenKit
|
||||
import BitwardenResources
|
||||
import SnapshotTesting
|
||||
@ -1,3 +1,4 @@
|
||||
// swiftlint:disable:this file_name
|
||||
import BitwardenKit
|
||||
import ViewInspector
|
||||
import XCTest
|
||||
@ -1,3 +1,4 @@
|
||||
// swiftlint:disable:this file_name
|
||||
import BitwardenKit
|
||||
import SwiftUI
|
||||
import ViewInspector
|
||||
@ -1,3 +1,4 @@
|
||||
// swiftlint:disable:this file_name
|
||||
import BitwardenKit
|
||||
import BitwardenResources
|
||||
import SnapshotTesting
|
||||
@ -1,3 +1,4 @@
|
||||
// swiftlint:disable:this file_name
|
||||
import BitwardenKit
|
||||
import SwiftUI
|
||||
import ViewInspector
|
||||
@ -1,3 +1,4 @@
|
||||
// swiftlint:disable:this file_name
|
||||
import BitwardenKit
|
||||
import SnapshotTesting
|
||||
import SwiftUI
|
||||
@ -1,6 +0,0 @@
|
||||
import SwiftUI
|
||||
import XCTest
|
||||
|
||||
@testable import BitwardenShareExtension
|
||||
|
||||
class ShareViewControllerTests: BitwardenTestCase {}
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
@ -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])
|
||||
}
|
||||
}
|
||||
@ -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])
|
||||
}
|
||||
}
|
||||
@ -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,
|
||||
],
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -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,
|
||||
],
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -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,
|
||||
],
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -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,
|
||||
],
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -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.
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
@ -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.
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
@ -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])
|
||||
}
|
||||
}
|
||||
@ -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])
|
||||
}
|
||||
}
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
@ -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])
|
||||
}
|
||||
}
|
||||
@ -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])
|
||||
}
|
||||
}
|
||||
73
BitwardenShared/UI/Auth/Login/LoginView+SnapshotTests.swift
Normal file
73
BitwardenShared/UI/Auth/Login/LoginView+SnapshotTests.swift
Normal 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)
|
||||
}
|
||||
}
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
@ -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])
|
||||
}
|
||||
}
|
||||
@ -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])
|
||||
}
|
||||
}
|
||||
@ -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],
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -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],
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -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.
|
||||
@ -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"))
|
||||
}
|
||||
}
|
||||
@ -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])
|
||||
}
|
||||
}
|
||||
@ -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])
|
||||
}
|
||||
}
|
||||
@ -2,7 +2,6 @@ import BitwardenKit
|
||||
import BitwardenKitMocks
|
||||
import BitwardenResources
|
||||
import BitwardenSdk
|
||||
import SnapshotTesting
|
||||
import SwiftUI
|
||||
import XCTest
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
// swiftlint:disable:this file_name
|
||||
import SnapshotTesting
|
||||
import SwiftUI
|
||||
import ViewInspector
|
||||
import XCTest
|
||||
|
||||
@testable import BitwardenShared
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
@ -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
|
||||
}
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
@ -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),
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -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],
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
@ -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],
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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)],
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -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)],
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -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(),
|
||||
],
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -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(),
|
||||
],
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -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),
|
||||
],
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -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),
|
||||
],
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
@ -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)],
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -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)],
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
@ -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
|
||||
|
||||
@ -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.
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
@ -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],
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -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],
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,3 +1,4 @@
|
||||
// swiftlint:disable:this file_name
|
||||
import SwiftUI
|
||||
import XCTest
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
// swiftlint:disable:this file_name
|
||||
import BitwardenKit
|
||||
import SwiftUI
|
||||
import ViewInspector
|
||||
@ -1,3 +1,4 @@
|
||||
// swiftlint:disable:this file_name
|
||||
import SnapshotTesting
|
||||
import XCTest
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
// swiftlint:disable:this file_name
|
||||
import BitwardenKitMocks
|
||||
import SnapshotTesting
|
||||
import XCTest
|
||||
@ -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
Loading…
x
Reference in New Issue
Block a user