diff options
| author | Niklas Berglund <niklas.berglund@gmail.com> | 2024-01-22 17:57:48 +0100 |
|---|---|---|
| committer | Bug Magnet <marco.nikic@mullvad.net> | 2024-01-31 16:21:05 +0100 |
| commit | ac131e3b68998baf24889defe4532d9fd8fcf294 (patch) | |
| tree | eb943a2694f19cb21bb9d37efa85306a037fa661 | |
| parent | 894f9ee16841292a722076be9ea6db2397f6f15c (diff) | |
| download | mullvadvpn-ac131e3b68998baf24889defe4532d9fd8fcf294.tar.xz mullvadvpn-ac131e3b68998baf24889defe4532d9fd8fcf294.zip | |
Add setup and teardown for iOS UI tests
17 files changed, 388 insertions, 140 deletions
diff --git a/ios/Configurations/Base.xcconfig.template b/ios/Configurations/Base.xcconfig.template index 7ba421b459..bb222523f0 100644 --- a/ios/Configurations/Base.xcconfig.template +++ b/ios/Configurations/Base.xcconfig.template @@ -4,6 +4,9 @@ // Development team DEVELOPMENT_TEAM = CKG9MXH72F +// Product display name +DISPLAY_NAME = Mullvad VPN + // Bundle ID APPLICATION_IDENTIFIER = net.mullvad.MullvadVPN diff --git a/ios/MullvadVPN.xcodeproj/project.pbxproj b/ios/MullvadVPN.xcodeproj/project.pbxproj index b84f9dba52..eaa1f58e83 100644 --- a/ios/MullvadVPN.xcodeproj/project.pbxproj +++ b/ios/MullvadVPN.xcodeproj/project.pbxproj @@ -565,8 +565,8 @@ 850201DB2B503D7700EF8C96 /* RelayTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 850201DA2B503D7700EF8C96 /* RelayTests.swift */; }; 850201DD2B503D8C00EF8C96 /* SelectLocationPage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 850201DC2B503D8C00EF8C96 /* SelectLocationPage.swift */; }; 850201DF2B5040A500EF8C96 /* TunnelControlPage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 850201DE2B5040A500EF8C96 /* TunnelControlPage.swift */; }; - 850201E12B51389500EF8C96 /* BaseUITestCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 850201E02B51389500EF8C96 /* BaseUITestCase.swift */; }; 850201E32B51A93C00EF8C96 /* SettingsPage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 850201E22B51A93C00EF8C96 /* SettingsPage.swift */; }; + 8518F6382B60157E009EB113 /* LoggedInWithoutTimeUITestCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8518F6372B60157E009EB113 /* LoggedInWithoutTimeUITestCase.swift */; }; 852969282B4D9C1F007EAD4C /* AccountTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 852969272B4D9C1F007EAD4C /* AccountTests.swift */; }; 852969332B4E9232007EAD4C /* Page.swift in Sources */ = {isa = PBXBuildFile; fileRef = 852969322B4E9232007EAD4C /* Page.swift */; }; 852969352B4E9270007EAD4C /* LoginPage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 852969342B4E9270007EAD4C /* LoginPage.swift */; }; @@ -574,6 +574,12 @@ 8529693A2B4F0238007EAD4C /* TermsOfServicePage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 852969392B4F0238007EAD4C /* TermsOfServicePage.swift */; }; 8529693C2B4F0257007EAD4C /* Alert.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8529693B2B4F0257007EAD4C /* Alert.swift */; }; 85557B162B5ABBBE00795FE1 /* XCUIElementQuery+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85557B152B5ABBBE00795FE1 /* XCUIElementQuery+Extensions.swift */; }; + 85557B1E2B5FB8C700795FE1 /* HeaderBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85557B1D2B5FB8C700795FE1 /* HeaderBar.swift */; }; + 85557B202B5FBBD700795FE1 /* AccountPage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85557B1F2B5FBBD700795FE1 /* AccountPage.swift */; }; + 8590896C2B61763B003AF5F5 /* LoggedInWithoutTimeUITestCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 859089682B61763B003AF5F5 /* LoggedInWithoutTimeUITestCase.swift */; }; + 8590896D2B61763B003AF5F5 /* LoggedInWithTimeUITestCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 859089692B61763B003AF5F5 /* LoggedInWithTimeUITestCase.swift */; }; + 8590896E2B61763B003AF5F5 /* BaseUITestCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8590896A2B61763B003AF5F5 /* BaseUITestCase.swift */; }; + 8590896F2B61763B003AF5F5 /* LoggedOutUITestCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8590896B2B61763B003AF5F5 /* LoggedOutUITestCase.swift */; }; A900E9B82ACC5C2B00C95F67 /* AccountsProxy+Stubs.swift in Sources */ = {isa = PBXBuildFile; fileRef = A900E9B72ACC5C2B00C95F67 /* AccountsProxy+Stubs.swift */; }; A900E9BA2ACC5D0600C95F67 /* RESTRequestExecutor+Stubs.swift in Sources */ = {isa = PBXBuildFile; fileRef = A900E9B92ACC5D0600C95F67 /* RESTRequestExecutor+Stubs.swift */; }; A900E9BC2ACC609200C95F67 /* DevicesProxy+Stubs.swift in Sources */ = {isa = PBXBuildFile; fileRef = A900E9BB2ACC609200C95F67 /* DevicesProxy+Stubs.swift */; }; @@ -1718,8 +1724,8 @@ 850201DA2B503D7700EF8C96 /* RelayTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelayTests.swift; sourceTree = "<group>"; }; 850201DC2B503D8C00EF8C96 /* SelectLocationPage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectLocationPage.swift; sourceTree = "<group>"; }; 850201DE2B5040A500EF8C96 /* TunnelControlPage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TunnelControlPage.swift; sourceTree = "<group>"; }; - 850201E02B51389500EF8C96 /* BaseUITestCase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseUITestCase.swift; sourceTree = "<group>"; }; 850201E22B51A93C00EF8C96 /* SettingsPage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsPage.swift; sourceTree = "<group>"; }; + 8518F6372B60157E009EB113 /* LoggedInWithoutTimeUITestCase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoggedInWithoutTimeUITestCase.swift; sourceTree = "<group>"; }; 852969252B4D9C1F007EAD4C /* MullvadVPNUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = MullvadVPNUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 852969272B4D9C1F007EAD4C /* AccountTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountTests.swift; sourceTree = "<group>"; }; 852969302B4D9E70007EAD4C /* MullvadVPNUITests.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = MullvadVPNUITests.xctestplan; sourceTree = "<group>"; }; @@ -1730,6 +1736,12 @@ 852969392B4F0238007EAD4C /* TermsOfServicePage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TermsOfServicePage.swift; sourceTree = "<group>"; }; 8529693B2B4F0257007EAD4C /* Alert.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Alert.swift; sourceTree = "<group>"; }; 85557B152B5ABBBE00795FE1 /* XCUIElementQuery+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "XCUIElementQuery+Extensions.swift"; sourceTree = "<group>"; }; + 85557B1D2B5FB8C700795FE1 /* HeaderBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HeaderBar.swift; sourceTree = "<group>"; }; + 85557B1F2B5FBBD700795FE1 /* AccountPage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountPage.swift; sourceTree = "<group>"; }; + 859089682B61763B003AF5F5 /* LoggedInWithoutTimeUITestCase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoggedInWithoutTimeUITestCase.swift; sourceTree = "<group>"; }; + 859089692B61763B003AF5F5 /* LoggedInWithTimeUITestCase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoggedInWithTimeUITestCase.swift; sourceTree = "<group>"; }; + 8590896A2B61763B003AF5F5 /* BaseUITestCase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BaseUITestCase.swift; sourceTree = "<group>"; }; + 8590896B2B61763B003AF5F5 /* LoggedOutUITestCase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoggedOutUITestCase.swift; sourceTree = "<group>"; }; A900E9B72ACC5C2B00C95F67 /* AccountsProxy+Stubs.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AccountsProxy+Stubs.swift"; sourceTree = "<group>"; }; A900E9B92ACC5D0600C95F67 /* RESTRequestExecutor+Stubs.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "RESTRequestExecutor+Stubs.swift"; sourceTree = "<group>"; }; A900E9BB2ACC609200C95F67 /* DevicesProxy+Stubs.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DevicesProxy+Stubs.swift"; sourceTree = "<group>"; }; @@ -3300,15 +3312,27 @@ path = RelayFilter; sourceTree = "<group>"; }; + 8518F6392B601910009EB113 /* Test base classes */ = { + isa = PBXGroup; + children = ( + 8590896A2B61763B003AF5F5 /* BaseUITestCase.swift */, + 859089682B61763B003AF5F5 /* LoggedInWithoutTimeUITestCase.swift */, + 859089692B61763B003AF5F5 /* LoggedInWithTimeUITestCase.swift */, + 8590896B2B61763B003AF5F5 /* LoggedOutUITestCase.swift */, + 8518F6372B60157E009EB113 /* LoggedInWithoutTimeUITestCase.swift */, + ); + path = "Test base classes"; + sourceTree = "<group>"; + }; 852969262B4D9C1F007EAD4C /* MullvadVPNUITests */ = { isa = PBXGroup; children = ( - 852969372B4ED20E007EAD4C /* Info.plist */, + 8518F6392B601910009EB113 /* Test base classes */, 852969312B4E9220007EAD4C /* Pages */, 852969272B4D9C1F007EAD4C /* AccountTests.swift */, - 850201E02B51389500EF8C96 /* BaseUITestCase.swift */, 850201DA2B503D7700EF8C96 /* RelayTests.swift */, 85557B152B5ABBBE00795FE1 /* XCUIElementQuery+Extensions.swift */, + 852969372B4ED20E007EAD4C /* Info.plist */, ); path = MullvadVPNUITests; sourceTree = "<group>"; @@ -3321,8 +3345,10 @@ 852969322B4E9232007EAD4C /* Page.swift */, 850201DC2B503D8C00EF8C96 /* SelectLocationPage.swift */, 850201E22B51A93C00EF8C96 /* SettingsPage.swift */, - 852969392B4F0238007EAD4C /* TermsOfServicePage.swift */, 850201DE2B5040A500EF8C96 /* TunnelControlPage.swift */, + 85557B1D2B5FB8C700795FE1 /* HeaderBar.swift */, + 852969392B4F0238007EAD4C /* TermsOfServicePage.swift */, + 85557B1F2B5FBBD700795FE1 /* AccountPage.swift */, ); path = Pages; sourceTree = "<group>"; @@ -5178,17 +5204,23 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 8590896D2B61763B003AF5F5 /* LoggedInWithTimeUITestCase.swift in Sources */, 8529693C2B4F0257007EAD4C /* Alert.swift in Sources */, 850201DD2B503D8C00EF8C96 /* SelectLocationPage.swift in Sources */, 850201DB2B503D7700EF8C96 /* RelayTests.swift in Sources */, 852969362B4E9724007EAD4C /* AccessbilityIdentifier.swift in Sources */, + 8590896C2B61763B003AF5F5 /* LoggedInWithoutTimeUITestCase.swift in Sources */, + 8590896F2B61763B003AF5F5 /* LoggedOutUITestCase.swift in Sources */, + 85557B202B5FBBD700795FE1 /* AccountPage.swift in Sources */, 852969352B4E9270007EAD4C /* LoginPage.swift in Sources */, - 850201E12B51389500EF8C96 /* BaseUITestCase.swift in Sources */, + 8590896E2B61763B003AF5F5 /* BaseUITestCase.swift in Sources */, 850201E32B51A93C00EF8C96 /* SettingsPage.swift in Sources */, + 8518F6382B60157E009EB113 /* LoggedInWithoutTimeUITestCase.swift in Sources */, 852969282B4D9C1F007EAD4C /* AccountTests.swift in Sources */, 85557B162B5ABBBE00795FE1 /* XCUIElementQuery+Extensions.swift in Sources */, 8529693A2B4F0238007EAD4C /* TermsOfServicePage.swift in Sources */, 850201DF2B5040A500EF8C96 /* TunnelControlPage.swift in Sources */, + 85557B1E2B5FB8C700795FE1 /* HeaderBar.swift in Sources */, 852969332B4E9232007EAD4C /* Page.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/ios/MullvadVPN/Classes/AccessbilityIdentifier.swift b/ios/MullvadVPN/Classes/AccessbilityIdentifier.swift index dd0fe98a35..4f31461e5e 100644 --- a/ios/MullvadVPN/Classes/AccessbilityIdentifier.swift +++ b/ios/MullvadVPN/Classes/AccessbilityIdentifier.swift @@ -26,6 +26,7 @@ public enum AccessibilityIdentifier: String { case logoutButton case purchaseButton case redeemVoucherButton + case restorePurchasesButton case selectLocationButton case settingsButton case startUsingTheAppButton @@ -44,8 +45,10 @@ public enum AccessibilityIdentifier: String { case headerDeviceNameLabel // Views + case accountView case alertContainerView case alertTitle + case headerBarView case loginView case termsOfServiceView case selectLocationView diff --git a/ios/MullvadVPN/Containers/Root/HeaderBarView.swift b/ios/MullvadVPN/Containers/Root/HeaderBarView.swift index 1f701cbede..476ec47eb5 100644 --- a/ios/MullvadVPN/Containers/Root/HeaderBarView.swift +++ b/ios/MullvadVPN/Containers/Root/HeaderBarView.swift @@ -170,6 +170,7 @@ class HeaderBarView: UIView { ) accessibilityContainerType = .semanticGroup + accessibilityIdentifier = .headerBarView let brandImageSize = brandNameImage?.size ?? .zero let brandNameAspectRatio = brandImageSize.width / max(brandImageSize.height, 1) diff --git a/ios/MullvadVPN/Supporting Files/Info.plist b/ios/MullvadVPN/Supporting Files/Info.plist index 3c3dd00b6f..b6b1ffa003 100644 --- a/ios/MullvadVPN/Supporting Files/Info.plist +++ b/ios/MullvadVPN/Supporting Files/Info.plist @@ -6,8 +6,6 @@ <string>The app needs this to connect and test a new method.</string> <key>ApplicationSecurityGroupIdentifier</key> <string>$(SECURITY_GROUP_IDENTIFIER)</string> - <key>MainApplicationIdentifier</key> - <string>$(APPLICATION_IDENTIFIER)</string> <key>BGTaskSchedulerPermittedIdentifiers</key> <array> <string>$(APPLICATION_IDENTIFIER).AppRefresh</string> @@ -17,7 +15,7 @@ <key>CFBundleDevelopmentRegion</key> <string>$(DEVELOPMENT_LANGUAGE)</string> <key>CFBundleDisplayName</key> - <string>Mullvad VPN</string> + <string>$(DISPLAY_NAME)</string> <key>CFBundleExecutable</key> <string>$(EXECUTABLE_NAME)</string> <key>CFBundleIdentifier</key> @@ -36,12 +34,61 @@ <false/> <key>LSRequiresIPhoneOS</key> <true/> + <key>MainApplicationIdentifier</key> + <string>$(APPLICATION_IDENTIFIER)</string> + <key>NSAppTransportSecurity</key> + <dict> + <key>NSAllowsArbitraryLoads</key> + <true/> + <key>NSExceptionDomains</key> + <dict> + <key>127.0.0.1</key> + <dict> + <key>NSExceptionAllowsInsecureHTTPLoads</key> + <true/> + </dict> + <key>45.83.223.196</key> + <dict> + <key>NSExceptionAllowsInsecureHTTPLoads</key> + <true/> + </dict> + </dict> + <key>NSPinnedDomains</key> + <dict> + <key>am.i.mullvad.net</key> + <dict> + <key>NSIncludesSubdomains</key> + <true/> + <key>NSPinnedCAIdentities</key> + <array> + <dict> + <key>SPKI-SHA256-BASE64</key> + <string>C5+lpZ7tcVwmwQIMcRtPbsQtWLABXhQzejna0wHFr8M=</string> + </dict> + </array> + </dict> + </dict> + </dict> <key>NSUserActivityTypes</key> <array> <string>StartVPNIntent</string> <string>StopVPNIntent</string> <string>ReconnectVPNIntent</string> </array> + <key>OSLogPreferences</key> + <dict> + <key>com.apple.VectorKit</key> + <dict> + <key>DEFAULT-OPTIONS</key> + <dict> + <key>Level</key> + <dict> + <key>Enable</key> + <string>Off</string> + </dict> + </dict> + </dict> + </dict> <key>UIApplicationSceneManifest</key> <dict> <key>UIApplicationSupportsMultipleScenes</key> @@ -71,52 +118,5 @@ <string>UIInterfaceOrientationLandscapeLeft</string> <string>UIInterfaceOrientationLandscapeRight</string> </array> - <key>NSAppTransportSecurity</key> - <dict> - <key>NSAllowsArbitraryLoads</key> - <true/> - <key>NSPinnedDomains</key> - <dict> - <key>am.i.mullvad.net</key> - <dict> - <key>NSIncludesSubdomains</key> - <true/> - <key>NSPinnedCAIdentities</key> - <array> - <dict> - <key>SPKI-SHA256-BASE64</key> - <string>C5+lpZ7tcVwmwQIMcRtPbsQtWLABXhQzejna0wHFr8M=</string> - </dict> - </array> - </dict> - </dict> - <key>NSExceptionDomains</key> - <dict> - <key>127.0.0.1</key> - <dict> - <key>NSExceptionAllowsInsecureHTTPLoads</key> - <true/> - </dict> - <key>45.83.223.196</key> - <dict> - <key>NSExceptionAllowsInsecureHTTPLoads</key> - <true/> - </dict> - </dict> - </dict> - <key>OSLogPreferences</key> - <dict> - <key>com.apple.VectorKit</key> - <dict> - <key>DEFAULT-OPTIONS</key> - <dict> - <key>Level</key> - <dict> - <key>Enable</key> - <string>Off</string> - </dict> - </dict> - </dict> - </dict> </dict> </plist> diff --git a/ios/MullvadVPN/View controllers/Account/AccountContentView.swift b/ios/MullvadVPN/View controllers/Account/AccountContentView.swift index 55cb501096..3d8ae81512 100644 --- a/ios/MullvadVPN/View controllers/Account/AccountContentView.swift +++ b/ios/MullvadVPN/View controllers/Account/AccountContentView.swift @@ -19,6 +19,7 @@ class AccountContentView: UIView { let restorePurchasesButton: AppButton = { let button = AppButton(style: .default) button.translatesAutoresizingMaskIntoConstraints = false + button.accessibilityIdentifier = .restorePurchasesButton button.setTitle(NSLocalizedString( "RESTORE_PURCHASES_BUTTON_TITLE", tableName: "Account", @@ -121,6 +122,7 @@ class AccountContentView: UIView { super.init(frame: frame) directionalLayoutMargins = UIMetrics.contentLayoutMargins + accessibilityIdentifier = .accountView addConstrainedSubviews([contentStackView, buttonStackView]) { contentStackView.pinEdgesToSuperviewMargins(.all().excluding(.bottom)) diff --git a/ios/MullvadVPNUITests/AccountTests.swift b/ios/MullvadVPNUITests/AccountTests.swift index bb719c91a9..319ce23509 100644 --- a/ios/MullvadVPNUITests/AccountTests.swift +++ b/ios/MullvadVPNUITests/AccountTests.swift @@ -8,23 +8,14 @@ import XCTest -class AccountTests: BaseUITestCase { +class AccountTests: LoggedOutUITestCase { override func setUpWithError() throws { continueAfterFailure = false - } - override func tearDownWithError() throws {} + try super.setUpWithError() + } func testLogin() throws { - let app = XCUIApplication() - app.launch() - - TermsOfServicePage(app) - .tapAgreeButton() - - Alert(app) - .tapOkay() - LoginPage(app) .tapAccountNumberTextField() .enterText(self.noTimeAccountNumber) @@ -34,15 +25,6 @@ class AccountTests: BaseUITestCase { } func testLoginWithIncorrectAccountNumber() throws { - let app = XCUIApplication() - app.launch() - - TermsOfServicePage(app) - .tapAgreeButton() - - Alert(app) - .tapOkay() - LoginPage(app) .tapAccountNumberTextField() .enterText("0000000000000000") diff --git a/ios/MullvadVPNUITests/BaseUITestCase.swift b/ios/MullvadVPNUITests/BaseUITestCase.swift deleted file mode 100644 index 83df4591cf..0000000000 --- a/ios/MullvadVPNUITests/BaseUITestCase.swift +++ /dev/null @@ -1,40 +0,0 @@ -// -// BaseTestCase.swift -// MullvadVPNUITests -// -// Created by Niklas Berglund on 2024-01-12. -// Copyright © 2024 Mullvad VPN AB. All rights reserved. -// - -import Foundation -import XCTest - -class BaseUITestCase: XCTestCase { - public static let defaultTimeout = 10.0 - - // swiftlint:disable force_cast - let noTimeAccountNumber = Bundle(for: BaseUITestCase.self) - .infoDictionary?["MullvadNoTimeAccountNumber"] as! String - let hasTimeAccountNumber = Bundle(for: BaseUITestCase.self) - .infoDictionary?["MullvadHasTimeAccountNumber"] as! String - let fiveWireGuardKeysAccountNumber = Bundle(for: BaseUITestCase.self) - .infoDictionary?["MullvadFiveWireGuardKeysAccountNumber"] as! String - let iOSDevicePinCode = Bundle(for: BaseUITestCase.self) - .infoDictionary?["MullvadIOSDevicePinCode"] as! String - let adServingDomain = Bundle(for: BaseUITestCase.self) - .infoDictionary?["MullvadAdServingDomain"] as! String - // swiftlint:enable force_cast - - /// Handle iOS add VPN configuration permission alert - allow and enter device PIN code - func allowAddVPNConfigurations() { - let springboard = XCUIApplication(bundleIdentifier: "com.apple.springboard") - - let alertAllowButton = springboard.buttons.element(boundBy: 0) - if alertAllowButton.waitForExistence(timeout: Self.defaultTimeout) { - alertAllowButton.tap() - } - - _ = springboard.buttons["1"].waitForExistence(timeout: Self.defaultTimeout) - springboard.typeText(iOSDevicePinCode) - } -} diff --git a/ios/MullvadVPNUITests/Info.plist b/ios/MullvadVPNUITests/Info.plist index 568e195a8f..85eb161b49 100644 --- a/ios/MullvadVPNUITests/Info.plist +++ b/ios/MullvadVPNUITests/Info.plist @@ -4,6 +4,8 @@ <dict> <key>MullvadAdServingDomain</key> <string>$(MULLVAD_AD_SERVING_DOMAIN)</string> + <key>MullvadDisplayName</key> + <string>$(DISPLAY_NAME)</string> <key>MullvadFiveWireGuardKeysAccountNumber</key> <string>$(MULLVAD_FIVE_WIREGUARD_KEYS_ACCOUNT_NUMBER)</string> <key>MullvadHasTimeAccountNumber</key> diff --git a/ios/MullvadVPNUITests/Pages/AccountPage.swift b/ios/MullvadVPNUITests/Pages/AccountPage.swift new file mode 100644 index 0000000000..8ff5e935af --- /dev/null +++ b/ios/MullvadVPNUITests/Pages/AccountPage.swift @@ -0,0 +1,44 @@ +// +// AccountPage.swift +// MullvadVPNUITests +// +// Created by Niklas Berglund on 2024-01-23. +// Copyright © 2024 Mullvad VPN AB. All rights reserved. +// + +import Foundation +import XCTest + +class AccountPage: Page { + @discardableResult override init(_ app: XCUIApplication) { + super.init(app) + + self.pageAccessibilityIdentifier = .accountView + waitForPageToBeShown() + } + + @discardableResult func tapRedeemVoucherButton() -> Self { + app.buttons[AccessibilityIdentifier.redeemVoucherButton.rawValue].tap() + return self + } + + @discardableResult func tapAdd30DaysTimeButton() -> Self { + app.buttons[AccessibilityIdentifier.purchaseButton.rawValue].tap() + return self + } + + @discardableResult func tapRestorePurchasesButton() -> Self { + app.buttons[AccessibilityIdentifier.restorePurchasesButton.rawValue].tap() + return self + } + + @discardableResult func tapLogOutButton() -> Self { + app.buttons[AccessibilityIdentifier.logoutButton.rawValue].tap() + return self + } + + @discardableResult func tapDeleteAccountButton() -> Self { + app.buttons[AccessibilityIdentifier.deleteButton.rawValue].tap() + return self + } +} diff --git a/ios/MullvadVPNUITests/Pages/HeaderBar.swift b/ios/MullvadVPNUITests/Pages/HeaderBar.swift new file mode 100644 index 0000000000..cf0473af8c --- /dev/null +++ b/ios/MullvadVPNUITests/Pages/HeaderBar.swift @@ -0,0 +1,29 @@ +// +// HeaderBar.swift +// MullvadVPNUITests +// +// Created by Niklas Berglund on 2024-01-23. +// Copyright © 2024 Mullvad VPN AB. All rights reserved. +// + +import Foundation +import XCTest + +class HeaderBar: Page { + @discardableResult override init(_ app: XCUIApplication) { + super.init(app) + + self.pageAccessibilityIdentifier = .headerBarView + waitForPageToBeShown() + } + + @discardableResult func tapAccountButton() -> Self { + app.buttons[AccessibilityIdentifier.accountButton.rawValue].tap() + return self + } + + @discardableResult func tapSettingsButton() -> Self { + app.buttons[AccessibilityIdentifier.settingsButton.rawValue].tap() + return self + } +} diff --git a/ios/MullvadVPNUITests/Pages/TunnelControlPage.swift b/ios/MullvadVPNUITests/Pages/TunnelControlPage.swift index 8fbee875bf..6469ed2232 100644 --- a/ios/MullvadVPNUITests/Pages/TunnelControlPage.swift +++ b/ios/MullvadVPNUITests/Pages/TunnelControlPage.swift @@ -20,9 +20,4 @@ class TunnelControlPage: Page { app.buttons[AccessibilityIdentifier.selectLocationButton].tap() return self } - - @discardableResult func tapSettingsButton() -> Self { - app.buttons[AccessibilityIdentifier.settingsButton].tap() - return self - } } diff --git a/ios/MullvadVPNUITests/RelayTests.swift b/ios/MullvadVPNUITests/RelayTests.swift index 45d3477e2c..c4e2ab777a 100644 --- a/ios/MullvadVPNUITests/RelayTests.swift +++ b/ios/MullvadVPNUITests/RelayTests.swift @@ -9,24 +9,8 @@ import Foundation import XCTest -class RelayTests: BaseUITestCase { +class RelayTests: LoggedInWithTimeUITestCase { func testAdBlockingViaDNS() throws { - let app = XCUIApplication() - app.launch() - - TermsOfServicePage(app) - .tapAgreeButton() - - Alert(app) - .tapOkay() - - LoginPage(app) - .tapAccountNumberTextField() - .enterText(self.hasTimeAccountNumber) - .tapAccountNumberSubmitButton() - .verifySuccessIconShown() - .verifyDeviceLabelShown() - TunnelControlPage(app) .tapSelectLocationButton() @@ -41,7 +25,7 @@ class RelayTests: BaseUITestCase { verifyCanReachAdServingDomain() - TunnelControlPage(app) + HeaderBar(app) .tapSettingsButton() SettingsPage(app) diff --git a/ios/MullvadVPNUITests/Test base classes/BaseUITestCase.swift b/ios/MullvadVPNUITests/Test base classes/BaseUITestCase.swift new file mode 100644 index 0000000000..cedba952dc --- /dev/null +++ b/ios/MullvadVPNUITests/Test base classes/BaseUITestCase.swift @@ -0,0 +1,138 @@ +// +// BaseTestCase.swift +// MullvadVPNUITests +// +// Created by Niklas Berglund on 2024-01-12. +// Copyright © 2024 Mullvad VPN AB. All rights reserved. +// + +import Foundation +import XCTest + +class BaseUITestCase: XCTestCase { + let app = XCUIApplication() + static let defaultTimeout = 5.0 + + // swiftlint:disable force_cast + let displayName = Bundle(for: BaseUITestCase.self) + .infoDictionary?["MullvadDisplayName"] as! String + let noTimeAccountNumber = Bundle(for: BaseUITestCase.self) + .infoDictionary?["MullvadNoTimeAccountNumber"] as! String + let hasTimeAccountNumber = Bundle(for: BaseUITestCase.self) + .infoDictionary?["MullvadHasTimeAccountNumber"] as! String + let fiveWireGuardKeysAccountNumber = Bundle(for: BaseUITestCase.self) + .infoDictionary?["MullvadFiveWireGuardKeysAccountNumber"] as! String + let iOSDevicePinCode = Bundle(for: BaseUITestCase.self) + .infoDictionary?["MullvadIOSDevicePinCode"] as! String + let adServingDomain = Bundle(for: BaseUITestCase.self) + .infoDictionary?["MullvadAdServingDomain"] as! String + // swiftlint:enable force_cast + + /// Handle iOS add VPN configuration permission alert - allow and enter device PIN code + func allowAddVPNConfigurations() { + let springboard = XCUIApplication(bundleIdentifier: "com.apple.springboard") + + let alertAllowButton = springboard.buttons.element(boundBy: 0) + if alertAllowButton.waitForExistence(timeout: Self.defaultTimeout) { + alertAllowButton.tap() + } + + _ = springboard.buttons["1"].waitForExistence(timeout: Self.defaultTimeout) + springboard.typeText(iOSDevicePinCode) + } + + // MARK: - Setup & teardown + + /// Suite level teardown ran after test have executed + override class func tearDown() { + uninstallApp() + } + + /// Test level setup + override func setUp() { + app.launch() + } + + /// Test level teardown + override func tearDown() { + app.terminate() + } + + /// Check if currently logged on to an account. Note that it is assumed that we are logged in if login view isn't currently shown. + func isLoggedIn() -> Bool { + return !app + .otherElements[AccessibilityIdentifier.loginView.rawValue] + .waitForExistence(timeout: 1.0) + } + + func agreeToTermsOfServiceIfShown() { + let termsOfServiceIsShown = app.otherElements[ + AccessibilityIdentifier + .termsOfServiceView.rawValue + ] + .waitForExistence(timeout: 1) + + if termsOfServiceIsShown { + TermsOfServicePage(app) + .tapAgreeButton() + + Alert(app) // Changes alert + .tapOkay() + + LoginPage(app) + } + } + + /// Login with specified account number. It is a prerequisite that the login page is currently shown. + func login(accountNumber: String) { + LoginPage(app) + .tapAccountNumberTextField() + .enterText(accountNumber) + .tapAccountNumberSubmitButton() + .verifyDeviceLabelShown() + } + + func logoutIfLoggedIn() { + if isLoggedIn() { + HeaderBar(app) + .tapAccountButton() + + AccountPage(app) + .tapLogOutButton() + + LoginPage(app) + } + } + + static func uninstallApp() { + let appName = "Mullvad VPN" + + let timeout = TimeInterval(5) + let springboard = XCUIApplication(bundleIdentifier: "com.apple.springboard") + let spotlight = XCUIApplication(bundleIdentifier: "com.apple.Spotlight") + + springboard.swipeDown() + spotlight.textFields["SpotlightSearchField"].typeText(appName) + + let appIcon = spotlight.icons[appName].firstMatch + if appIcon.waitForExistence(timeout: timeout) { + appIcon.press(forDuration: 2) + } else { + XCTFail("Failed to find app icon named \(appName)") + } + + let deleteAppButton = spotlight.buttons["Delete App"] + if deleteAppButton.waitForExistence(timeout: timeout) { + deleteAppButton.tap() + } else { + XCTFail("Failed to find 'Delete App'") + } + + let finalDeleteButton = springboard.alerts.buttons["Delete"] + if finalDeleteButton.waitForExistence(timeout: timeout) { + finalDeleteButton.tap() + } else { + XCTFail("Failed to find 'Delete'") + } + } +} diff --git a/ios/MullvadVPNUITests/Test base classes/LoggedInWithTimeUITestCase.swift b/ios/MullvadVPNUITests/Test base classes/LoggedInWithTimeUITestCase.swift new file mode 100644 index 0000000000..fc1fa747f8 --- /dev/null +++ b/ios/MullvadVPNUITests/Test base classes/LoggedInWithTimeUITestCase.swift @@ -0,0 +1,25 @@ +// +// LoggedInUITestCase.swift +// MullvadVPNUITests +// +// Created by Niklas Berglund on 2024-01-22. +// Copyright © 2024 Mullvad VPN AB. All rights reserved. +// + +import Foundation + +/// Base class for tests that should start from a state of being logged on to an account with time left +class LoggedInWithTimeUITestCase: BaseUITestCase { + override func setUp() { + super.setUp() + + agreeToTermsOfServiceIfShown() + logoutIfLoggedIn() + + login(accountNumber: hasTimeAccountNumber) + + // Relaunch app so that tests start from a deterministic state + app.terminate() + app.launch() + } +} diff --git a/ios/MullvadVPNUITests/Test base classes/LoggedInWithoutTimeUITestCase.swift b/ios/MullvadVPNUITests/Test base classes/LoggedInWithoutTimeUITestCase.swift new file mode 100644 index 0000000000..c7ab76e31b --- /dev/null +++ b/ios/MullvadVPNUITests/Test base classes/LoggedInWithoutTimeUITestCase.swift @@ -0,0 +1,25 @@ +// +// LoggedInWithoutTimeUITestCase.swift +// MullvadVPNUITests +// +// Created by Niklas Berglund on 2024-01-23. +// Copyright © 2024 Mullvad VPN AB. All rights reserved. +// + +import Foundation + +/// Base class for tests that should start from a state of being logged on to an account without time left +class LoggedInWithoutTimeUITestCase: BaseUITestCase { + override func setUp() { + super.setUp() + + agreeToTermsOfServiceIfShown() + logoutIfLoggedIn() + + login(accountNumber: noTimeAccountNumber) + + // Relaunch app so that tests start from a deterministic state + app.terminate() + app.launch() + } +} diff --git a/ios/MullvadVPNUITests/Test base classes/LoggedOutUITestCase.swift b/ios/MullvadVPNUITests/Test base classes/LoggedOutUITestCase.swift new file mode 100644 index 0000000000..a89c6e732e --- /dev/null +++ b/ios/MullvadVPNUITests/Test base classes/LoggedOutUITestCase.swift @@ -0,0 +1,23 @@ +// +// LoggedOutUITestCase.swift +// MullvadVPNUITests +// +// Created by Niklas Berglund on 2024-01-22. +// Copyright © 2024 Mullvad VPN AB. All rights reserved. +// + +import Foundation + +/// Base class for tests which should start from a logged out state +class LoggedOutUITestCase: BaseUITestCase { + override func setUp() { + super.setUp() + + agreeToTermsOfServiceIfShown() + logoutIfLoggedIn() + + // Relaunch app so that tests start from a deterministic state + app.terminate() + app.launch() + } +} |
