Skip to content

Commit

Permalink
Add iOS test for connecting to API via bridges
Browse files Browse the repository at this point in the history
  • Loading branch information
niklasberglund committed Feb 7, 2024
1 parent 446ef2b commit 59c9def
Show file tree
Hide file tree
Showing 18 changed files with 550 additions and 79 deletions.
17 changes: 11 additions & 6 deletions ios/Configurations/UITests.xcconfig.template
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,18 @@
//

// Pin code of the iOS device under test
MULLVAD_IOS_DEVICE_PIN_CODE =
IOS_DEVICE_PIN_CODE =

// Ad serving domain used when testing ad blocking. Not that we are assuming there's an HTTP server running on the host.
MULLVAD_AD_SERVING_DOMAIN =
// UUID to identify test runs. Should be unique per test device. Generate with for example uuidgen on macOS.
TEST_DEVICE_IDENTIFIER_UUID =

// Ad serving domain used when testing ad blocking. Note that we are assuming there's an HTTP server running on the host.
AD_SERVING_DOMAIN =

// Mullvad accounts used by UI tests
MULLVAD_NO_TIME_ACCOUNT_NUMBER =
MULLVAD_HAS_TIME_ACCOUNT_NUMBER =
MULLVAD_FIVE_WIREGUARD_KEYS_ACCOUNT_NUMBER =
NO_TIME_ACCOUNT_NUMBER =
HAS_TIME_ACCOUNT_NUMBER =
FIVE_WIREGUARD_KEYS_ACCOUNT_NUMBER =

// Base URL for the firewall API, Note that // will be treated as a comment, therefor you need to insert a ${} between the slashes for example http:/${}/8.8.8.8
FIREWALL_API_BASE_URL = http:/${}/8.8.8.8
37 changes: 35 additions & 2 deletions ios/MullvadVPN.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -582,13 +582,19 @@
852969362B4E9724007EAD4C /* AccessbilityIdentifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A0B311D2B303A0D004B12E0 /* AccessbilityIdentifier.swift */; };
8529693A2B4F0238007EAD4C /* TermsOfServicePage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 852969392B4F0238007EAD4C /* TermsOfServicePage.swift */; };
8529693C2B4F0257007EAD4C /* Alert.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8529693B2B4F0257007EAD4C /* Alert.swift */; };
85557B0E2B591B2600795FE1 /* FirewallAPIClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85557B0D2B591B2600795FE1 /* FirewallAPIClient.swift */; };
85557B102B59215F00795FE1 /* FirewallRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85557B0F2B59215F00795FE1 /* FirewallRule.swift */; };
85557B122B594FC900795FE1 /* ConnectivityTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85557B112B594FC900795FE1 /* ConnectivityTests.swift */; };
85557B142B5983CF00795FE1 /* MullvadAPIWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85557B132B5983CF00795FE1 /* MullvadAPIWrapper.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 */; };
855D9F5B2B63E56B00D7C64D /* ProblemReportPage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 855D9F5A2B63E56B00D7C64D /* ProblemReportPage.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 */; };
85D2B0B12B6BD32400DF9DA7 /* BaseUITestCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8590896A2B61763B003AF5F5 /* BaseUITestCase.swift */; };
85E3BDE52B70E18C00FA71FD /* NetworkTester.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85E3BDE42B70E18C00FA71FD /* NetworkTester.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 */; };
Expand Down Expand Up @@ -1755,13 +1761,19 @@
852969382B4ED818007EAD4C /* UITests.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = UITests.xcconfig; sourceTree = "<group>"; };
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>"; };
85557B0D2B591B2600795FE1 /* FirewallAPIClient.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FirewallAPIClient.swift; sourceTree = "<group>"; };
85557B0F2B59215F00795FE1 /* FirewallRule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FirewallRule.swift; sourceTree = "<group>"; };
85557B112B594FC900795FE1 /* ConnectivityTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConnectivityTests.swift; sourceTree = "<group>"; };
85557B132B5983CF00795FE1 /* MullvadAPIWrapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MullvadAPIWrapper.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>"; };
855D9F5A2B63E56B00D7C64D /* ProblemReportPage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProblemReportPage.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>"; };
85E3BDE42B70E18C00FA71FD /* NetworkTester.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkTester.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>"; };
Expand Down Expand Up @@ -3352,11 +3364,13 @@
isa = PBXGroup;
children = (
8518F6392B601910009EB113 /* Test base classes */,
85557B0C2B591B0F00795FE1 /* Networking */,
852969312B4E9220007EAD4C /* Pages */,
852969272B4D9C1F007EAD4C /* AccountTests.swift */,
850201DA2B503D7700EF8C96 /* RelayTests.swift */,
85557B152B5ABBBE00795FE1 /* XCUIElementQuery+Extensions.swift */,
852969372B4ED20E007EAD4C /* Info.plist */,
85557B112B594FC900795FE1 /* ConnectivityTests.swift */,
);
path = MullvadVPNUITests;
sourceTree = "<group>";
Expand All @@ -3373,10 +3387,22 @@
85557B1D2B5FB8C700795FE1 /* HeaderBar.swift */,
852969392B4F0238007EAD4C /* TermsOfServicePage.swift */,
85557B1F2B5FBBD700795FE1 /* AccountPage.swift */,
855D9F5A2B63E56B00D7C64D /* ProblemReportPage.swift */,
);
path = Pages;
sourceTree = "<group>";
};
85557B0C2B591B0F00795FE1 /* Networking */ = {
isa = PBXGroup;
children = (
85557B0D2B591B2600795FE1 /* FirewallAPIClient.swift */,
85557B0F2B59215F00795FE1 /* FirewallRule.swift */,
85557B132B5983CF00795FE1 /* MullvadAPIWrapper.swift */,
85E3BDE42B70E18C00FA71FD /* NetworkTester.swift */,
);
path = Networking;
sourceTree = "<group>";
};
A907639F2B2857D50045ADF0 /* Socks5 */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -5239,23 +5265,30 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
85D2B0B12B6BD32400DF9DA7 /* BaseUITestCase.swift in Sources */,
8590896D2B61763B003AF5F5 /* LoggedInWithTimeUITestCase.swift in Sources */,
8529693C2B4F0257007EAD4C /* Alert.swift in Sources */,
850201DD2B503D8C00EF8C96 /* SelectLocationPage.swift in Sources */,
850201DB2B503D7700EF8C96 /* RelayTests.swift in Sources */,
85557B142B5983CF00795FE1 /* MullvadAPIWrapper.swift in Sources */,
852969362B4E9724007EAD4C /* AccessbilityIdentifier.swift in Sources */,
85E3BDE52B70E18C00FA71FD /* NetworkTester.swift in Sources */,
8590896C2B61763B003AF5F5 /* LoggedInWithoutTimeUITestCase.swift in Sources */,
8590896F2B61763B003AF5F5 /* LoggedOutUITestCase.swift in Sources */,
85557B202B5FBBD700795FE1 /* AccountPage.swift in Sources */,
852969352B4E9270007EAD4C /* LoginPage.swift in Sources */,
8590896E2B61763B003AF5F5 /* BaseUITestCase.swift in Sources */,
850201E32B51A93C00EF8C96 /* SettingsPage.swift in Sources */,
8518F6382B60157E009EB113 /* LoggedInWithoutTimeUITestCase.swift in Sources */,
85557B102B59215F00795FE1 /* FirewallRule.swift in Sources */,
850201E32B51A93C00EF8C96 /* SettingsPage.swift in Sources */,
85557B0E2B591B2600795FE1 /* FirewallAPIClient.swift in Sources */,
852969282B4D9C1F007EAD4C /* AccountTests.swift in Sources */,
85557B162B5ABBBE00795FE1 /* XCUIElementQuery+Extensions.swift in Sources */,
855D9F5B2B63E56B00D7C64D /* ProblemReportPage.swift in Sources */,
8529693A2B4F0238007EAD4C /* TermsOfServicePage.swift in Sources */,
850201DF2B5040A500EF8C96 /* TunnelControlPage.swift in Sources */,
85557B1E2B5FB8C700795FE1 /* HeaderBar.swift in Sources */,
85557B122B594FC900795FE1 /* ConnectivityTests.swift in Sources */,
852969332B4E9232007EAD4C /* Page.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
Expand Down
7 changes: 7 additions & 0 deletions ios/MullvadVPN/Classes/AccessbilityIdentifier.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,12 @@ public enum AccessibilityIdentifier: String {
case purchaseButton
case redeemVoucherButton
case restorePurchasesButton
case secureConnectionButton
case selectLocationButton
case settingsButton
case startUsingTheAppButton
case problemReportAppLogsButton
case problemReportSendButton

// Cells
case preferencesCell
Expand All @@ -43,6 +46,7 @@ public enum AccessibilityIdentifier: String {

// Labels
case headerDeviceNameLabel
case connectionStatusLabel

// Views
case accountView
Expand All @@ -55,6 +59,7 @@ public enum AccessibilityIdentifier: String {
case selectLocationTableView
case settingsTableView
case tunnelControlView
case problemReportView

// Other UI elements
case connectionPanelInAddressRow
Expand All @@ -63,6 +68,8 @@ public enum AccessibilityIdentifier: String {
case dnsContentBlockersHeaderView
case loginTextField
case selectLocationSearchTextField
case problemReportEmailTextField
case problemReportMessageTextView

// DNS settings
case dnsSettings
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ final class ProblemReportViewController: UIViewController, UITextFieldDelegate {
private lazy var viewLogsButton: AppButton = {
let button = AppButton(style: .default)
button.translatesAutoresizingMaskIntoConstraints = false
button.accessibilityIdentifier = .problemReportAppLogsButton
button.setTitle(NSLocalizedString(
"VIEW_APP_LOGS_BUTTON_TITLE",
tableName: "ProblemReport",
Expand All @@ -149,6 +150,7 @@ final class ProblemReportViewController: UIViewController, UITextFieldDelegate {
private lazy var sendButton: AppButton = {
let button = AppButton(style: .success)
button.translatesAutoresizingMaskIntoConstraints = false
button.accessibilityIdentifier = .problemReportSendButton
button.setTitle(NSLocalizedString(
"SEND_BUTTON_TITLE",
tableName: "ProblemReport",
Expand Down Expand Up @@ -210,6 +212,7 @@ final class ProblemReportViewController: UIViewController, UITextFieldDelegate {
super.viewDidLoad()

view.backgroundColor = .secondaryColor
view.accessibilityIdentifier = .problemReportView

navigationItem.title = NSLocalizedString(
"NAVIGATION_TITLE",
Expand All @@ -230,6 +233,9 @@ final class ProblemReportViewController: UIViewController, UITextFieldDelegate {
messageTextView.setContentHuggingPriority(.defaultLow, for: .vertical)
messageTextView.setContentCompressionResistancePriority(.defaultLow, for: .vertical)

emailTextField.accessibilityIdentifier = .problemReportEmailTextField
messageTextView.accessibilityIdentifier = .problemReportMessageTextView

addConstraints()
registerForNotifications()
loadPersistentViewModel()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ final class TunnelControlView: UIView {

private let connectButton: AppButton = {
let button = AppButton(style: .success)
button.accessibilityIdentifier = .secureConnectionButton
button.translatesAutoresizingMaskIntoConstraints = false
return button
}()
Expand Down Expand Up @@ -120,6 +121,8 @@ final class TunnelControlView: UIView {
accessibilityContainerType = .semanticGroup
accessibilityIdentifier = .tunnelControlView

secureLabel.accessibilityIdentifier = .connectionStatusLabel

addSubviews()
addButtonHandlers()
}
Expand Down
52 changes: 52 additions & 0 deletions ios/MullvadVPNUITests/ConnectivityTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
//
// ConnectivityTests.swift
// MullvadVPNUITests
//
// Created by Niklas Berglund on 2024-01-18.
// Copyright © 2024 Mullvad VPN AB. All rights reserved.
//

import Foundation
import Network
import XCTest

class ConnectivityTests: LoggedOutUITestCase {
let firewallAPIClient = FirewallAPIClient()

override func setUpWithError() throws {
super.setUp()
}

override func tearDownWithError() throws {
super.tearDown()
firewallAPIClient.removeRules()
}

/// Verifies that the app still functions when API has been blocked
func testAPIConnectionViaBridges() throws {
let app = XCUIApplication()
app.launch()

NetworkTester.verifyCanAccessAPI() // Just to make sure there's no old firewall rule still active
firewallAPIClient.createRule(FirewallRule.makeBlockAPIAccessFirewallRule())
NetworkTester.verifyCannotAccessAPI()

LoginPage(app)
.tapAccountNumberTextField()
.enterText(self.hasTimeAccountNumber)
.tapAccountNumberSubmitButton()

// After creating firewall rule first login attempt might fail. One more attempt is allowed since the app is cycling between two methods.
if isLoggedIn() {
LoginPage(app)
.verifySuccessIconShown()
.verifyDeviceLabelShown()
} else {
LoginPage(app)
.verifyFailIconShown()
.tapAccountNumberSubmitButton()
.verifySuccessIconShown()
.verifyDeviceLabelShown()
}
}
}
30 changes: 19 additions & 11 deletions ios/MullvadVPNUITests/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,25 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>MullvadAdServingDomain</key>
<string>$(MULLVAD_AD_SERVING_DOMAIN)</string>
<key>MullvadDisplayName</key>
<key>AdServingDomain</key>
<string>$(AD_SERVING_DOMAIN)</string>
<key>ApiEndpoint</key>
<string>$(API_ENDPOINT)</string>
<key>ApiHostName</key>
<string>$(API_HOST_NAME)</string>
<key>DisplayName</key>
<string>$(DISPLAY_NAME)</string>
<key>MullvadFiveWireGuardKeysAccountNumber</key>
<string>$(MULLVAD_FIVE_WIREGUARD_KEYS_ACCOUNT_NUMBER)</string>
<key>MullvadHasTimeAccountNumber</key>
<string>$(MULLVAD_HAS_TIME_ACCOUNT_NUMBER)</string>
<key>MullvadIOSDevicePinCode</key>
<string>$(MULLVAD_IOS_DEVICE_PIN_CODE)</string>
<key>MullvadNoTimeAccountNumber</key>
<string>$(MULLVAD_NO_TIME_ACCOUNT_NUMBER)</string>
<key>FirewallApiBaseURL</key>
<string>$(FIREWALL_API_BASE_URL)</string>
<key>FiveWireGuardKeysAccountNumber</key>
<string>$(FIVE_WIREGUARD_KEYS_ACCOUNT_NUMBER)</string>
<key>HasTimeAccountNumber</key>
<string>$(HAS_TIME_ACCOUNT_NUMBER)</string>
<key>IOSDevicePinCode</key>
<string>$(IOS_DEVICE_PIN_CODE)</string>
<key>NoTimeAccountNumber</key>
<string>$(NO_TIME_ACCOUNT_NUMBER)</string>
<key>TestDeviceIdentifier</key>
<string>$(TEST_DEVICE_IDENTIFIER_UUID</string>
</dict>
</plist>
Loading

0 comments on commit 59c9def

Please sign in to comment.