diff --git a/.github/ISSUE_TEMPLATE/bug.yml b/.github/ISSUE_TEMPLATE/bug.yml index 07c08a7b8..fc5bae008 100644 --- a/.github/ISSUE_TEMPLATE/bug.yml +++ b/.github/ISSUE_TEMPLATE/bug.yml @@ -56,8 +56,8 @@ body: attributes: label: Environment Details placeholder: | - - Device: [e.g. iPhone 15 Pro, iPad Air (5th Generation)] - - OS Version: [e.g. 17.4.1] + - Device: [e.g. iPhone 16 Pro, iPad Air (5th Generation)] + - OS Version: [e.g. 18.0.1] - type: checkboxes id: issue-tracking-info attributes: diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 650217d95..88115f837 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -72,7 +72,7 @@ env: jobs: build: name: Build - runs-on: macos-14 + runs-on: macos-15 env: MINT_PATH: .mint/lib MINT_LINK_PATH: .mint/bin diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 0aebb0953..a1adf309a 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -47,7 +47,7 @@ jobs: test: name: Test - runs-on: macos-14-xlarge + runs-on: macos-15-xlarge needs: check-run permissions: contents: read diff --git a/.test-simulator-ios-version b/.test-simulator-ios-version index 0034b6527..33718932a 100644 --- a/.test-simulator-ios-version +++ b/.test-simulator-ios-version @@ -1 +1 @@ -18.0 +18.1 diff --git a/.xcode-version b/.xcode-version index 232a7fc1a..c32b0ec5a 100644 --- a/.xcode-version +++ b/.xcode-version @@ -1 +1 @@ -15.4 +16.1 diff --git a/BitwardenShared/UI/Auth/ProfileSwitcher/ProfileSwitcherViewTests.swift b/BitwardenShared/UI/Auth/ProfileSwitcher/ProfileSwitcherViewTests.swift index a977a52b0..fa78a63a7 100644 --- a/BitwardenShared/UI/Auth/ProfileSwitcher/ProfileSwitcherViewTests.swift +++ b/BitwardenShared/UI/Auth/ProfileSwitcher/ProfileSwitcherViewTests.swift @@ -42,7 +42,7 @@ class ProfileSwitcherViewTests: BitwardenTestCase { // swiftlint:disable:this ty @MainActor func test_accountRow_longPress_currentAccount() throws { let accountRow = try subject.inspect().find(button: "anne.account@bitwarden.com") - try accountRow.labelView().callOnLongPressGesture() + try accountRow.labelView().recursiveCallOnLongPressGesture() let currentAccount = processor.state.activeAccountProfile! waitFor(!processor.effects.isEmpty) @@ -54,14 +54,14 @@ class ProfileSwitcherViewTests: BitwardenTestCase { // swiftlint:disable:this ty func test_accountRow_longPress_currentAccount_noLockOrLogout() throws { processor.state.allowLockAndLogout = false let accountRow = try subject.inspect().find(button: "anne.account@bitwarden.com") - XCTAssertThrowsError(try accountRow.labelView().callOnLongPressGesture()) + XCTAssertThrowsError(try accountRow.labelView().recursiveCallOnLongPressGesture()) } /// 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() + try accountRow.labelView().recursiveCallOnTapGesture() let currentAccount = processor.state.activeAccountProfile! waitFor(!processor.effects.isEmpty) @@ -96,7 +96,7 @@ class ProfileSwitcherViewTests: BitwardenTestCase { // swiftlint:disable:this ty isVisible: true ) let alternateRow = try subject.inspect().find(button: "alternate@bitwarden.com") - try alternateRow.labelView().callOnLongPressGesture() + try alternateRow.labelView().recursiveCallOnLongPressGesture() waitFor(!processor.effects.isEmpty) XCTAssertEqual(processor.effects.last, .accountLongPressed(alternate)) @@ -121,7 +121,7 @@ class ProfileSwitcherViewTests: BitwardenTestCase { // swiftlint:disable:this ty ) let alternateRow = try subject.inspect().find(button: "alternate@bitwarden.com") _ = try subject.inspect().find(button: "anne.account@bitwarden.com") - XCTAssertThrowsError(try alternateRow.labelView().callOnLongPressGesture()) + XCTAssertThrowsError(try alternateRow.labelView().recursiveCallOnLongPressGesture()) } /// Tapping an alternative profile row dispatches the `.accountPressed` action. @@ -142,7 +142,7 @@ class ProfileSwitcherViewTests: BitwardenTestCase { // swiftlint:disable:this ty isVisible: true ) let addAccountRow = try subject.inspect().find(button: "alternate@bitwarden.com") - try addAccountRow.labelView().callOnTapGesture() + try addAccountRow.labelView().recursiveCallOnTapGesture() waitFor(!processor.effects.isEmpty) XCTAssertEqual(processor.effects.last, .accountPressed(alternate)) @@ -169,7 +169,7 @@ class ProfileSwitcherViewTests: BitwardenTestCase { // swiftlint:disable:this ty isVisible: true ) let addAccountRow = try subject.inspect().find(button: "") - try addAccountRow.labelView().callOnTapGesture() + try addAccountRow.labelView().recursiveCallOnTapGesture() waitFor(!processor.effects.isEmpty) XCTAssertEqual(processor.effects.last, .accountPressed(secondAlternate)) @@ -179,7 +179,7 @@ class ProfileSwitcherViewTests: BitwardenTestCase { // swiftlint:disable:this ty @MainActor func test_background_tap() throws { let view = try subject.inspect().view(ProfileSwitcherView.self) - let background = view.first + let background = try view.implicitAnyView().first try background?.callOnTapGesture() XCTAssertEqual(processor.dispatchedActions.last, .backgroundPressed) diff --git a/BitwardenShared/UI/Platform/Application/Extensions/__Snapshots__/UITabBarControllerExtensionsTests/test_snapshot_tabBarItems_generatorSelected_darkMode.1.png b/BitwardenShared/UI/Platform/Application/Extensions/__Snapshots__/UITabBarControllerExtensionsTests/test_snapshot_tabBarItems_generatorSelected_darkMode.1.png index b03fbc6e7..e0177050b 100644 Binary files a/BitwardenShared/UI/Platform/Application/Extensions/__Snapshots__/UITabBarControllerExtensionsTests/test_snapshot_tabBarItems_generatorSelected_darkMode.1.png and b/BitwardenShared/UI/Platform/Application/Extensions/__Snapshots__/UITabBarControllerExtensionsTests/test_snapshot_tabBarItems_generatorSelected_darkMode.1.png differ diff --git a/BitwardenShared/UI/Platform/Application/Extensions/__Snapshots__/UITabBarControllerExtensionsTests/test_snapshot_tabBarItems_generatorSelected_lightMode.1.png b/BitwardenShared/UI/Platform/Application/Extensions/__Snapshots__/UITabBarControllerExtensionsTests/test_snapshot_tabBarItems_generatorSelected_lightMode.1.png index d456bb18d..148467201 100644 Binary files a/BitwardenShared/UI/Platform/Application/Extensions/__Snapshots__/UITabBarControllerExtensionsTests/test_snapshot_tabBarItems_generatorSelected_lightMode.1.png and b/BitwardenShared/UI/Platform/Application/Extensions/__Snapshots__/UITabBarControllerExtensionsTests/test_snapshot_tabBarItems_generatorSelected_lightMode.1.png differ diff --git a/BitwardenShared/UI/Platform/Application/Extensions/__Snapshots__/UITabBarControllerExtensionsTests/test_snapshot_tabBarItems_sendSelected_darkMode.1.png b/BitwardenShared/UI/Platform/Application/Extensions/__Snapshots__/UITabBarControllerExtensionsTests/test_snapshot_tabBarItems_sendSelected_darkMode.1.png index 0c148f52b..4cb0ce7bb 100644 Binary files a/BitwardenShared/UI/Platform/Application/Extensions/__Snapshots__/UITabBarControllerExtensionsTests/test_snapshot_tabBarItems_sendSelected_darkMode.1.png and b/BitwardenShared/UI/Platform/Application/Extensions/__Snapshots__/UITabBarControllerExtensionsTests/test_snapshot_tabBarItems_sendSelected_darkMode.1.png differ diff --git a/BitwardenShared/UI/Platform/Application/Extensions/__Snapshots__/UITabBarControllerExtensionsTests/test_snapshot_tabBarItems_sendSelected_lightMode.1.png b/BitwardenShared/UI/Platform/Application/Extensions/__Snapshots__/UITabBarControllerExtensionsTests/test_snapshot_tabBarItems_sendSelected_lightMode.1.png index 02e451b1f..2b757937f 100644 Binary files a/BitwardenShared/UI/Platform/Application/Extensions/__Snapshots__/UITabBarControllerExtensionsTests/test_snapshot_tabBarItems_sendSelected_lightMode.1.png and b/BitwardenShared/UI/Platform/Application/Extensions/__Snapshots__/UITabBarControllerExtensionsTests/test_snapshot_tabBarItems_sendSelected_lightMode.1.png differ diff --git a/BitwardenShared/UI/Platform/Application/Extensions/__Snapshots__/UITabBarControllerExtensionsTests/test_snapshot_tabBarItems_settingsSelected_darkMode.1.png b/BitwardenShared/UI/Platform/Application/Extensions/__Snapshots__/UITabBarControllerExtensionsTests/test_snapshot_tabBarItems_settingsSelected_darkMode.1.png index bc6c2b91b..f3df815e4 100644 Binary files a/BitwardenShared/UI/Platform/Application/Extensions/__Snapshots__/UITabBarControllerExtensionsTests/test_snapshot_tabBarItems_settingsSelected_darkMode.1.png and b/BitwardenShared/UI/Platform/Application/Extensions/__Snapshots__/UITabBarControllerExtensionsTests/test_snapshot_tabBarItems_settingsSelected_darkMode.1.png differ diff --git a/BitwardenShared/UI/Platform/Application/Extensions/__Snapshots__/UITabBarControllerExtensionsTests/test_snapshot_tabBarItems_settingsSelected_lightMode.1.png b/BitwardenShared/UI/Platform/Application/Extensions/__Snapshots__/UITabBarControllerExtensionsTests/test_snapshot_tabBarItems_settingsSelected_lightMode.1.png index 58801a4a0..7e651e5b9 100644 Binary files a/BitwardenShared/UI/Platform/Application/Extensions/__Snapshots__/UITabBarControllerExtensionsTests/test_snapshot_tabBarItems_settingsSelected_lightMode.1.png and b/BitwardenShared/UI/Platform/Application/Extensions/__Snapshots__/UITabBarControllerExtensionsTests/test_snapshot_tabBarItems_settingsSelected_lightMode.1.png differ diff --git a/BitwardenShared/UI/Platform/Application/Extensions/__Snapshots__/UITabBarControllerExtensionsTests/test_snapshot_tabBarItems_vaultSelected_darkMode.1.png b/BitwardenShared/UI/Platform/Application/Extensions/__Snapshots__/UITabBarControllerExtensionsTests/test_snapshot_tabBarItems_vaultSelected_darkMode.1.png index 100053233..a27807bfc 100644 Binary files a/BitwardenShared/UI/Platform/Application/Extensions/__Snapshots__/UITabBarControllerExtensionsTests/test_snapshot_tabBarItems_vaultSelected_darkMode.1.png and b/BitwardenShared/UI/Platform/Application/Extensions/__Snapshots__/UITabBarControllerExtensionsTests/test_snapshot_tabBarItems_vaultSelected_darkMode.1.png differ diff --git a/BitwardenShared/UI/Platform/Application/Extensions/__Snapshots__/UITabBarControllerExtensionsTests/test_snapshot_tabBarItems_vaultSelected_lightMode.1.png b/BitwardenShared/UI/Platform/Application/Extensions/__Snapshots__/UITabBarControllerExtensionsTests/test_snapshot_tabBarItems_vaultSelected_lightMode.1.png index 585e02596..96bf5037d 100644 Binary files a/BitwardenShared/UI/Platform/Application/Extensions/__Snapshots__/UITabBarControllerExtensionsTests/test_snapshot_tabBarItems_vaultSelected_lightMode.1.png and b/BitwardenShared/UI/Platform/Application/Extensions/__Snapshots__/UITabBarControllerExtensionsTests/test_snapshot_tabBarItems_vaultSelected_lightMode.1.png differ diff --git a/BitwardenShared/UI/Platform/Settings/Settings/Vault/ExportVault/ExportVaultViewTests.swift b/BitwardenShared/UI/Platform/Settings/Settings/Vault/ExportVault/ExportVaultViewTests.swift index 9a5ab2bca..efe4c7fa5 100644 --- a/BitwardenShared/UI/Platform/Settings/Settings/Vault/ExportVault/ExportVaultViewTests.swift +++ b/BitwardenShared/UI/Platform/Settings/Settings/Vault/ExportVault/ExportVaultViewTests.swift @@ -48,8 +48,8 @@ class ExportVaultViewTests: BitwardenTestCase { let menuField = try subject.inspect().find(bitwardenMenuField: Localizations.fileFormat) XCTAssertTrue(menuField.isDisabled()) - let textfield = try subject.inspect().find(viewWithId: Localizations.masterPassword).textField() - XCTAssertTrue(textfield.isDisabled()) + let secureField = try subject.inspect().find(viewWithId: Localizations.masterPassword).secureField() + XCTAssertTrue(secureField.isDisabled()) } /// Tapping the export vault button sends the `.exportVault` action. diff --git a/BitwardenShared/UI/Vault/Vault/VaultList/VaultListViewTests.swift b/BitwardenShared/UI/Vault/Vault/VaultList/VaultListViewTests.swift index 99fd2616d..8575a925f 100644 --- a/BitwardenShared/UI/Vault/Vault/VaultList/VaultListViewTests.swift +++ b/BitwardenShared/UI/Vault/Vault/VaultList/VaultListViewTests.swift @@ -82,7 +82,7 @@ class VaultListViewTests: BitwardenTestCase { // swiftlint:disable:this type_bod processor.state.profileSwitcherState.isVisible = true let accountRow = try subject.inspect().find(button: "anne.account@bitwarden.com") let currentAccount = processor.state.profileSwitcherState.activeAccountProfile! - try accountRow.labelView().callOnLongPressGesture() + try accountRow.labelView().recursiveCallOnLongPressGesture() waitFor(!processor.effects.isEmpty) XCTAssertEqual(processor.effects.last, .profileSwitcher(.accountLongPressed(currentAccount))) @@ -94,7 +94,7 @@ class VaultListViewTests: BitwardenTestCase { // swiftlint:disable:this type_bod processor.state.profileSwitcherState.isVisible = true let accountRow = try subject.inspect().find(button: "anne.account@bitwarden.com") let currentAccount = processor.state.profileSwitcherState.activeAccountProfile! - try accountRow.labelView().callOnTapGesture() + try accountRow.labelView().recursiveCallOnTapGesture() waitFor(!processor.effects.isEmpty) XCTAssertEqual(processor.effects.last, .profileSwitcher(.accountPressed(currentAccount))) diff --git a/BitwardenShared/UI/Vault/Vault/VaultList/__Snapshots__/VaultListViewTests/test_snapshot_vaultListView_previews.5.png b/BitwardenShared/UI/Vault/Vault/VaultList/__Snapshots__/VaultListViewTests/test_snapshot_vaultListView_previews.5.png index 7789c3c35..4c2f64a1b 100644 Binary files a/BitwardenShared/UI/Vault/Vault/VaultList/__Snapshots__/VaultListViewTests/test_snapshot_vaultListView_previews.5.png and b/BitwardenShared/UI/Vault/Vault/VaultList/__Snapshots__/VaultListViewTests/test_snapshot_vaultListView_previews.5.png differ diff --git a/Docs/Architecture.md b/Docs/Architecture.md index fc040ece9..32f6e3b1a 100644 --- a/Docs/Architecture.md +++ b/Docs/Architecture.md @@ -320,4 +320,4 @@ Every type containing logic should be tested. Test files should be named ` InspectableView { + try find(ViewType.Button.self) + } + + /// Overrides the default `secureField` method in order to find a text field + /// that might be buried beneath added `AnyView` objects. + func secureField() throws -> InspectableView { + try find(ViewType.SecureField.self) + } + + /// Overrides the default `text` method in order to find a text field + /// that might be buried beneath added `AnyView` objects. + func text() throws -> InspectableView { + try find(ViewType.Text.self) + } + + /// Overrides the default `textField` method in order to find a text field + /// that might be buried beneath added `AnyView` objects. + func textField() throws -> InspectableView { + try find(ViewType.TextField.self) + } +} + +extension InspectableView where View: SingleViewContent { + /// Recursively traverses a child view hierarchy of `AnyView` objects until + /// it finds one that will take a long press gesture, and performs the gesture. + /// This is necessary because Xcode 16 adds additional `AnyView` objects in + /// debug mode. + func recursiveCallOnLongPressGesture() throws { + do { + try callOnLongPressGesture() + } catch { + try implicitAnyView().recursiveCallOnLongPressGesture() + } + } + + /// Recursively traverses a child view hierarchy of `AnyView` objects until + /// it finds one that will take a tap gesture, and performs the gesture. + /// This is necessary because Xcode 16 adds additional `AnyView` objects in + /// debug mode. + func recursiveCallOnTapGesture() throws { + do { + try callOnTapGesture() + } catch { + try implicitAnyView().recursiveCallOnTapGesture() + } + } +} + extension InspectableView where View == AsyncButtonType { /// Simulates a tap on an `AsyncButton`. This method is asynchronous and allows the entire `async` `action` on the /// button to run before returning. diff --git a/GlobalTestHelpers/Extensions/Snapshotting.swift b/GlobalTestHelpers/Extensions/Snapshotting.swift index c55b1de5a..054796340 100644 --- a/GlobalTestHelpers/Extensions/Snapshotting.swift +++ b/GlobalTestHelpers/Extensions/Snapshotting.swift @@ -174,7 +174,8 @@ extension Snapshotting where Value == UIViewController, Format == UIImage { static var standardImage: Snapshotting { .image( precision: defaultPrecision, - perceptualPrecision: defaultPerceptualPrecision + perceptualPrecision: defaultPerceptualPrecision, + size: ViewImageConfig.iPhone13(.portrait).size ) } } diff --git a/GlobalTestHelpers/Support/BitwardenTestCase.swift b/GlobalTestHelpers/Support/BitwardenTestCase.swift index 9b852298b..4e8dc6481 100644 --- a/GlobalTestHelpers/Support/BitwardenTestCase.swift +++ b/GlobalTestHelpers/Support/BitwardenTestCase.swift @@ -9,13 +9,13 @@ open class BitwardenTestCase: XCTestCase { @MainActor override open class func setUp() { - if UIDevice.current.name != "iPhone 15 Pro" { - assertionFailure( - """ - Tests must be run using the iPhone 15 Pro simulator. Snapshot tests depend on using the correct device. - """ - ) - } + if UIDevice.current.name != "iPhone 15 Pro" || UIDevice.current.systemVersion != "18.1" { + assertionFailure( + """ + Tests must be run using iOS 18.1 on an iPhone 15 Pro simulator. Snapshot tests depend on using the correct device. + """ + ) + } // Apply default appearances for snapshot tests. UI.applyDefaultAppearances() diff --git a/README.md b/README.md index b8c0e1a9f..ab2af99ff 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ - **Minimum iOS**: 15.0 - **Target SDK**: 15.0 - **Device Types Supported**: iPhone, iPad -- **Screen Sizes Supported**: iPhone SE to iPhone 15 Pro Max, iPad Mini to iPad Pro 12.9" +- **Screen Sizes Supported**: iPhone SE to iPhone 16 Pro Max, iPad Mini to iPad Pro 12.9" - **Orientations Supported**: Portrait, Landscape ## Setup @@ -66,7 +66,7 @@ ### Running Tests -Due to slight snapshot test variations between iOS version, the test target requires running in an iPhone 15 Pro simulator (iOS 18). +The test target requires running in an iPhone 15 Pro simulator running iOS 18.1. It is, however, worth noting that our snapshot tests almost entirely work off of enforced iPhone 12/13/14 dimensions, with a 3x scale; however a few use the dimensions of the chosen simulator, and thus require an iPhone 15 Pro. 1. In Xcode's toolbar, select the project and a connected device or simulator. - The `Generic iOS Device` used for builds will not work for testing.