summaryrefslogtreecommitdiffhomepage
path: root/ios
diff options
context:
space:
mode:
authorBug Magnet <marco.nikic@mullvad.net>2024-01-24 14:23:15 +0100
committerBug Magnet <marco.nikic@mullvad.net>2024-01-24 14:23:15 +0100
commitf4ec9f8a2b1e1390b8580eaaeee6d9cb283909fa (patch)
treeabfb4bf071f45c475d9937d5b8956a4836fab65b /ios
parentf4d2f443d792cf4b871d871a20a509f80c9f0434 (diff)
parent3a5ba4a0d80810567a02c43c442913505759757d (diff)
downloadmullvadvpn-f4ec9f8a2b1e1390b8580eaaeee6d9cb283909fa.tar.xz
mullvadvpn-f4ec9f8a2b1e1390b8580eaaeee6d9cb283909fa.zip
Merge branch 'ad-blocking-via-dns-ios-440'
Diffstat (limited to 'ios')
-rw-r--r--ios/Configurations/UITests.xcconfig.template8
-rw-r--r--ios/MullvadVPN.xcodeproj/project.pbxproj26
-rw-r--r--ios/MullvadVPN/Classes/AccessbilityIdentifier.swift10
-rw-r--r--ios/MullvadVPN/View controllers/Preferences/CustomDNSDataSource.swift1
-rw-r--r--ios/MullvadVPN/View controllers/SelectLocation/SelectLocationViewController.swift2
-rw-r--r--ios/MullvadVPN/View controllers/Settings/SettingsViewController.swift1
-rw-r--r--ios/MullvadVPN/View controllers/Tunnel/ConnectionPanelView.swift5
-rw-r--r--ios/MullvadVPN/View controllers/Tunnel/TunnelControlView.swift1
-rw-r--r--ios/MullvadVPN/Views/CustomSwitch.swift2
-rw-r--r--ios/MullvadVPNUITests/BaseUITestCase.swift27
-rw-r--r--ios/MullvadVPNUITests/Info.plist4
-rw-r--r--ios/MullvadVPNUITests/Pages/Alert.swift2
-rw-r--r--ios/MullvadVPNUITests/Pages/LoginPage.swift8
-rw-r--r--ios/MullvadVPNUITests/Pages/Page.swift17
-rw-r--r--ios/MullvadVPNUITests/Pages/SelectLocationPage.swift34
-rw-r--r--ios/MullvadVPNUITests/Pages/SettingsPage.swift50
-rw-r--r--ios/MullvadVPNUITests/Pages/TermsOfServicePage.swift2
-rw-r--r--ios/MullvadVPNUITests/Pages/TunnelControlPage.swift28
-rw-r--r--ios/MullvadVPNUITests/RelayTests.swift96
-rw-r--r--ios/MullvadVPNUITests/XCUIElementQuery+Extensions.swift16
20 files changed, 318 insertions, 22 deletions
diff --git a/ios/Configurations/UITests.xcconfig.template b/ios/Configurations/UITests.xcconfig.template
index c8727dd3e1..2ed40f1624 100644
--- a/ios/Configurations/UITests.xcconfig.template
+++ b/ios/Configurations/UITests.xcconfig.template
@@ -6,8 +6,14 @@
// Copyright © 2024 Mullvad VPN AB. All rights reserved.
//
+// Pin code of the iOS device under test
+MULLVAD_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 =
+
// Mullvad accounts used by UI tests
MULLVAD_NO_TIME_ACCOUNT_NUMBER =
MULLVAD_HAS_TIME_ACCOUNT_NUMBER =
-MULLVAD_FIVE_WIREGUARD_KEYS_ACCOUNT_NUMBER =
+MULLVAD_FIVE_WIREGUARD_KEYS_ACCOUNT_NUMBER =
diff --git a/ios/MullvadVPN.xcodeproj/project.pbxproj b/ios/MullvadVPN.xcodeproj/project.pbxproj
index 6a43be8c0d..0e6a5225cb 100644
--- a/ios/MullvadVPN.xcodeproj/project.pbxproj
+++ b/ios/MullvadVPN.xcodeproj/project.pbxproj
@@ -568,13 +568,18 @@
7AF9BE902A39F26000DBFEDB /* Collection+Sorting.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AF9BE8F2A39F26000DBFEDB /* Collection+Sorting.swift */; };
7AF9BE952A40461100DBFEDB /* RelayFilterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AF9BE942A40461100DBFEDB /* RelayFilterView.swift */; };
7AF9BE972A41C71F00DBFEDB /* RelayFilterChipView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AF9BE962A41C71F00DBFEDB /* RelayFilterChipView.swift */; };
+ 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 */; };
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 */; };
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 */; };
+ 85557B162B5ABBBE00795FE1 /* XCUIElementQuery+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85557B152B5ABBBE00795FE1 /* XCUIElementQuery+Extensions.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 */; };
@@ -1720,7 +1725,11 @@
7AF9BE8F2A39F26000DBFEDB /* Collection+Sorting.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Collection+Sorting.swift"; sourceTree = "<group>"; };
7AF9BE942A40461100DBFEDB /* RelayFilterView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelayFilterView.swift; sourceTree = "<group>"; };
7AF9BE962A41C71F00DBFEDB /* RelayFilterChipView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelayFilterChipView.swift; sourceTree = "<group>"; };
+ 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>"; };
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 +1739,7 @@
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>"; };
+ 85557B152B5ABBBE00795FE1 /* XCUIElementQuery+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "XCUIElementQuery+Extensions.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>"; };
@@ -2937,6 +2947,7 @@
58CE5E62224146200008646E /* MullvadVPN */,
58D0C79423F1CE7000FE9BA7 /* MullvadVPNScreenshots */,
58B0A2A1238EE67E00BC001D /* MullvadVPNTests */,
+ 852969262B4D9C1F007EAD4C /* MullvadVPNUITests */,
58D223F4294C8FF00029F5F8 /* MullvadLogging */,
581943F228F8014500B0CB5E /* MullvadTypes */,
06799ABD28F98E1D00ACD94E /* MullvadREST */,
@@ -2952,7 +2963,6 @@
58C7A4432A863F490060C66F /* PacketTunnelCoreTests */,
7A88DCCF2A8FABBE00D2FF0E /* Routing */,
7A88DCDD2A8FABBE00D2FF0E /* RoutingTests */,
- 852969262B4D9C1F007EAD4C /* MullvadVPNUITests */,
58CE5E61224146200008646E /* Products */,
584F991F2902CBDD001F858D /* Frameworks */,
);
@@ -3333,6 +3343,8 @@
852969312B4E9220007EAD4C /* Pages */,
852969272B4D9C1F007EAD4C /* AccountTests.swift */,
850201E02B51389500EF8C96 /* BaseUITestCase.swift */,
+ 850201DA2B503D7700EF8C96 /* RelayTests.swift */,
+ 85557B152B5ABBBE00795FE1 /* XCUIElementQuery+Extensions.swift */,
);
path = MullvadVPNUITests;
sourceTree = "<group>";
@@ -3340,10 +3352,13 @@
852969312B4E9220007EAD4C /* Pages */ = {
isa = PBXGroup;
children = (
- 852969322B4E9232007EAD4C /* Page.swift */,
+ 8529693B2B4F0257007EAD4C /* Alert.swift */,
852969342B4E9270007EAD4C /* LoginPage.swift */,
+ 852969322B4E9232007EAD4C /* Page.swift */,
+ 850201DC2B503D8C00EF8C96 /* SelectLocationPage.swift */,
+ 850201E22B51A93C00EF8C96 /* SettingsPage.swift */,
852969392B4F0238007EAD4C /* TermsOfServicePage.swift */,
- 8529693B2B4F0257007EAD4C /* Alert.swift */,
+ 850201DE2B5040A500EF8C96 /* TunnelControlPage.swift */,
);
path = Pages;
sourceTree = "<group>";
@@ -5202,11 +5217,16 @@
buildActionMask = 2147483647;
files = (
8529693C2B4F0257007EAD4C /* Alert.swift in Sources */,
+ 850201DD2B503D8C00EF8C96 /* SelectLocationPage.swift in Sources */,
+ 850201DB2B503D7700EF8C96 /* RelayTests.swift in Sources */,
852969362B4E9724007EAD4C /* AccessbilityIdentifier.swift in Sources */,
852969352B4E9270007EAD4C /* LoginPage.swift in Sources */,
850201E12B51389500EF8C96 /* BaseUITestCase.swift in Sources */,
+ 850201E32B51A93C00EF8C96 /* SettingsPage.swift in Sources */,
852969282B4D9C1F007EAD4C /* AccountTests.swift in Sources */,
+ 85557B162B5ABBBE00795FE1 /* XCUIElementQuery+Extensions.swift in Sources */,
8529693A2B4F0238007EAD4C /* TermsOfServicePage.swift in Sources */,
+ 850201DF2B5040A500EF8C96 /* TunnelControlPage.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 7cb2e0e607..dd0fe98a35 100644
--- a/ios/MullvadVPN/Classes/AccessbilityIdentifier.swift
+++ b/ios/MullvadVPN/Classes/AccessbilityIdentifier.swift
@@ -15,6 +15,7 @@ public enum AccessibilityIdentifier: String {
case alertOkButton
case applyButton
case cancelButton
+ case connectionPanelButton
case collapseButton
case deleteButton
case disconnectButton
@@ -47,9 +48,18 @@ public enum AccessibilityIdentifier: String {
case alertTitle
case loginView
case termsOfServiceView
+ case selectLocationView
+ case selectLocationTableView
+ case settingsTableView
+ case tunnelControlView
// Other UI elements
+ case connectionPanelInAddressRow
+ case connectionPanelOutAddressRow
+ case customSwitch
+ case dnsContentBlockersHeaderView
case loginTextField
+ case selectLocationSearchTextField
// DNS settings
case dnsSettings
diff --git a/ios/MullvadVPN/View controllers/Preferences/CustomDNSDataSource.swift b/ios/MullvadVPN/View controllers/Preferences/CustomDNSDataSource.swift
index ea7217b1ea..afed9c5845 100644
--- a/ios/MullvadVPN/View controllers/Preferences/CustomDNSDataSource.swift
+++ b/ios/MullvadVPN/View controllers/Preferences/CustomDNSDataSource.swift
@@ -589,6 +589,7 @@ final class CustomDNSDataSource: UITableViewDiffableDataSource<
header.titleLabel.text = title
header.accessibilityCustomActionName = title
+ header.accessibilityIdentifier = .dnsContentBlockersHeaderView
header.infoButtonHandler = { [weak self] in
self?.delegate?.showInfo(for: .contentBlockers)
diff --git a/ios/MullvadVPN/View controllers/SelectLocation/SelectLocationViewController.swift b/ios/MullvadVPN/View controllers/SelectLocation/SelectLocationViewController.swift
index b19fef0df6..47e9a4309f 100644
--- a/ios/MullvadVPN/View controllers/SelectLocation/SelectLocationViewController.swift
+++ b/ios/MullvadVPN/View controllers/SelectLocation/SelectLocationViewController.swift
@@ -133,6 +133,7 @@ final class SelectLocationViewController: UIViewController {
tableView.estimatedRowHeight = 53
tableView.indicatorStyle = .white
tableView.keyboardDismissMode = .onDrag
+ tableView.accessibilityIdentifier = .selectLocationTableView
}
private func setUpTopContent() {
@@ -167,6 +168,7 @@ final class SelectLocationViewController: UIViewController {
value: "Search for...",
comment: ""
)
+ searchBar.searchTextField.accessibilityIdentifier = .selectLocationSearchTextField
UITextField.SearchTextFieldAppearance.inactive.apply(to: searchBar)
}
diff --git a/ios/MullvadVPN/View controllers/Settings/SettingsViewController.swift b/ios/MullvadVPN/View controllers/Settings/SettingsViewController.swift
index 7ac7167840..13f6f6d1e2 100644
--- a/ios/MullvadVPN/View controllers/Settings/SettingsViewController.swift
+++ b/ios/MullvadVPN/View controllers/Settings/SettingsViewController.swift
@@ -54,6 +54,7 @@ class SettingsViewController: UITableViewController, SettingsDataSourceDelegate
})
)
+ tableView.accessibilityIdentifier = .settingsTableView
tableView.backgroundColor = .secondaryColor
tableView.separatorColor = .secondaryColor
tableView.rowHeight = UITableView.automaticDimension
diff --git a/ios/MullvadVPN/View controllers/Tunnel/ConnectionPanelView.swift b/ios/MullvadVPN/View controllers/Tunnel/ConnectionPanelView.swift
index 7908906d3b..2e975c9c38 100644
--- a/ios/MullvadVPN/View controllers/Tunnel/ConnectionPanelView.swift
+++ b/ios/MullvadVPN/View controllers/Tunnel/ConnectionPanelView.swift
@@ -75,6 +75,9 @@ class ConnectionPanelView: UIView {
inAddressRow.translatesAutoresizingMaskIntoConstraints = false
outAddressRow.translatesAutoresizingMaskIntoConstraints = false
+ inAddressRow.accessibilityIdentifier = .connectionPanelInAddressRow
+ outAddressRow.accessibilityIdentifier = .connectionPanelOutAddressRow
+
inAddressRow.title = NSLocalizedString(
"IN_ADDRESS_LABEL",
tableName: "ConnectionPanel",
@@ -285,6 +288,8 @@ class ConnectionPanelCollapseButton: CustomButton {
imageAlignment = .trailing
inlineImageSpacing = 0
+ accessibilityIdentifier = .connectionPanelButton
+
updateButtonImage()
}
diff --git a/ios/MullvadVPN/View controllers/Tunnel/TunnelControlView.swift b/ios/MullvadVPN/View controllers/Tunnel/TunnelControlView.swift
index 75d2b012c8..e1f393b1b7 100644
--- a/ios/MullvadVPN/View controllers/Tunnel/TunnelControlView.swift
+++ b/ios/MullvadVPN/View controllers/Tunnel/TunnelControlView.swift
@@ -118,6 +118,7 @@ final class TunnelControlView: UIView {
backgroundColor = .clear
directionalLayoutMargins = UIMetrics.contentLayoutMargins
accessibilityContainerType = .semanticGroup
+ accessibilityIdentifier = .tunnelControlView
addSubviews()
addButtonHandlers()
diff --git a/ios/MullvadVPN/Views/CustomSwitch.swift b/ios/MullvadVPN/Views/CustomSwitch.swift
index 4596cb56b6..a696d52142 100644
--- a/ios/MullvadVPN/Views/CustomSwitch.swift
+++ b/ios/MullvadVPN/Views/CustomSwitch.swift
@@ -31,6 +31,8 @@ class CustomSwitch: UISwitch {
onTintColor = .clear
overrideUserInterfaceStyle = .light
+ accessibilityIdentifier = .customSwitch
+
updateThumbColor(isOn: isOn, animated: false)
addTarget(self, action: #selector(valueChanged(_:)), for: .valueChanged)
diff --git a/ios/MullvadVPNUITests/BaseUITestCase.swift b/ios/MullvadVPNUITests/BaseUITestCase.swift
index 7ca4356e03..83df4591cf 100644
--- a/ios/MullvadVPNUITests/BaseUITestCase.swift
+++ b/ios/MullvadVPNUITests/BaseUITestCase.swift
@@ -10,10 +10,31 @@ import Foundation
import XCTest
class BaseUITestCase: XCTestCase {
+ public static let defaultTimeout = 10.0
+
// swiftlint:disable force_cast
- let noTimeAccountNumber = Bundle(for: AccountTests.self).infoDictionary?["MullvadNoTimeAccountNumber"] as! String
- let hasTimeAccountNumber = Bundle(for: AccountTests.self).infoDictionary?["MullvadHasTimeAccountNumber"] as! String
- let fiveWireGuardKeysAccountNumber = Bundle(for: AccountTests.self)
+ 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 f0e3228b4b..568e195a8f 100644
--- a/ios/MullvadVPNUITests/Info.plist
+++ b/ios/MullvadVPNUITests/Info.plist
@@ -2,10 +2,14 @@
<!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>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>
</dict>
diff --git a/ios/MullvadVPNUITests/Pages/Alert.swift b/ios/MullvadVPNUITests/Pages/Alert.swift
index 7ecfb59e25..dce7c3bf11 100644
--- a/ios/MullvadVPNUITests/Pages/Alert.swift
+++ b/ios/MullvadVPNUITests/Pages/Alert.swift
@@ -19,7 +19,7 @@ class Alert: Page {
}
@discardableResult func tapOkay() -> Self {
- app.buttons[AccessibilityIdentifier.alertOkButton.rawValue].tap()
+ app.buttons[AccessibilityIdentifier.alertOkButton].tap()
return self
}
}
diff --git a/ios/MullvadVPNUITests/Pages/LoginPage.swift b/ios/MullvadVPNUITests/Pages/LoginPage.swift
index 7d13d88088..18a44b5e74 100644
--- a/ios/MullvadVPNUITests/Pages/LoginPage.swift
+++ b/ios/MullvadVPNUITests/Pages/LoginPage.swift
@@ -18,19 +18,19 @@ class LoginPage: Page {
}
@discardableResult public func tapAccountNumberTextField() -> Self {
- app.textFields[AccessibilityIdentifier.loginTextField.rawValue].tap()
+ app.textFields[AccessibilityIdentifier.loginTextField].tap()
return self
}
@discardableResult public func tapAccountNumberSubmitButton() -> Self {
- app.buttons[AccessibilityIdentifier.loginTextFieldButton.rawValue].tap()
+ app.buttons[AccessibilityIdentifier.loginTextFieldButton].tap()
return self
}
@discardableResult public func verifyDeviceLabelShown() -> Self {
XCTAssertTrue(
- app.staticTexts[AccessibilityIdentifier.headerDeviceNameLabel.rawValue]
- .waitForExistence(timeout: defaultTimeout)
+ app.staticTexts[AccessibilityIdentifier.headerDeviceNameLabel]
+ .waitForExistence(timeout: BaseUITestCase.defaultTimeout)
)
return self
diff --git a/ios/MullvadVPNUITests/Pages/Page.swift b/ios/MullvadVPNUITests/Pages/Page.swift
index 775edf0beb..6d39ba6805 100644
--- a/ios/MullvadVPNUITests/Pages/Page.swift
+++ b/ios/MullvadVPNUITests/Pages/Page.swift
@@ -12,23 +12,22 @@ import XCTest
class Page {
let app: XCUIApplication
var pageAccessibilityIdentifier: AccessibilityIdentifier?
- let defaultTimeout = 10.0
- init(_ app: XCUIApplication) {
+ @discardableResult init(_ app: XCUIApplication) {
self.app = app
}
- public func enterText(_ text: String) -> Self {
- app.typeText(text)
- return self
- }
-
public func waitForPageToBeShown() {
if let pageAccessibilityIdentifier = self.pageAccessibilityIdentifier {
XCTAssert(
- self.app.otherElements[pageAccessibilityIdentifier.rawValue]
- .waitForExistence(timeout: defaultTimeout)
+ self.app.otherElements[pageAccessibilityIdentifier]
+ .waitForExistence(timeout: BaseUITestCase.defaultTimeout)
)
}
}
+
+ @discardableResult public func enterText(_ text: String) -> Self {
+ app.typeText(text)
+ return self
+ }
}
diff --git a/ios/MullvadVPNUITests/Pages/SelectLocationPage.swift b/ios/MullvadVPNUITests/Pages/SelectLocationPage.swift
new file mode 100644
index 0000000000..f841101ff6
--- /dev/null
+++ b/ios/MullvadVPNUITests/Pages/SelectLocationPage.swift
@@ -0,0 +1,34 @@
+//
+// SelectLocationPage.swift
+// MullvadVPNUITests
+//
+// Created by Niklas Berglund on 2024-01-11.
+// Copyright © 2024 Mullvad VPN AB. All rights reserved.
+//
+
+import Foundation
+import XCTest
+
+class SelectLocationPage: Page {
+ @discardableResult override init(_ app: XCUIApplication) {
+ super.init(app)
+
+ self.pageAccessibilityIdentifier = .selectLocationView
+ }
+
+ @discardableResult func tapLocationCell(withName name: String) -> Self {
+ app.tables[AccessibilityIdentifier.selectLocationTableView].cells.staticTexts[name].tap()
+ return self
+ }
+
+ @discardableResult func tapLocationCellExpandButton(withName name: String) -> Self {
+ let table = app.tables[AccessibilityIdentifier.selectLocationTableView]
+ let matchingCells = table.cells.containing(.any, identifier: name)
+ let buttons = matchingCells.buttons
+ let expandButton = buttons[AccessibilityIdentifier.collapseButton]
+
+ expandButton.tap()
+
+ return self
+ }
+}
diff --git a/ios/MullvadVPNUITests/Pages/SettingsPage.swift b/ios/MullvadVPNUITests/Pages/SettingsPage.swift
new file mode 100644
index 0000000000..c57fd3ff04
--- /dev/null
+++ b/ios/MullvadVPNUITests/Pages/SettingsPage.swift
@@ -0,0 +1,50 @@
+//
+// SettingsPage.swift
+// MullvadVPNUITests
+//
+// Created by Niklas Berglund on 2024-01-12.
+// Copyright © 2024 Mullvad VPN AB. All rights reserved.
+//
+
+import Foundation
+import XCTest
+
+class SettingsPage: Page {
+ @discardableResult override init(_ app: XCUIApplication) {
+ super.init(app)
+
+ self.pageAccessibilityIdentifier = .settingsTableView
+ }
+
+ @discardableResult func tapVPNSettingsCell() -> Self {
+ app.tables[AccessibilityIdentifier.settingsTableView]
+ .cells[AccessibilityIdentifier.preferencesCell]
+ .tap()
+
+ return self
+ }
+
+ @discardableResult func tapDNSSettingsCell() -> Self {
+ app.tables
+ .cells[AccessibilityIdentifier.dnsSettings]
+ .tap()
+
+ return self
+ }
+
+ @discardableResult func tapDNSContentBlockingHeaderExpandButton() -> Self {
+ let headerView = app.otherElements[AccessibilityIdentifier.dnsContentBlockersHeaderView]
+ let expandButton = headerView.buttons[AccessibilityIdentifier.collapseButton]
+ expandButton.tap()
+
+ return self
+ }
+
+ @discardableResult func tapBlockAdsSwitch() -> Self {
+ app.cells[AccessibilityIdentifier.blockAdvertising]
+ .switches[AccessibilityIdentifier.customSwitch]
+ .tap()
+
+ return self
+ }
+}
diff --git a/ios/MullvadVPNUITests/Pages/TermsOfServicePage.swift b/ios/MullvadVPNUITests/Pages/TermsOfServicePage.swift
index fb64b3237d..b1a569290d 100644
--- a/ios/MullvadVPNUITests/Pages/TermsOfServicePage.swift
+++ b/ios/MullvadVPNUITests/Pages/TermsOfServicePage.swift
@@ -17,7 +17,7 @@ class TermsOfServicePage: Page {
}
@discardableResult func tapAgreeButton() -> Self {
- app.buttons[AccessibilityIdentifier.agreeButton.rawValue].tap()
+ app.buttons[AccessibilityIdentifier.agreeButton].tap()
return self
}
}
diff --git a/ios/MullvadVPNUITests/Pages/TunnelControlPage.swift b/ios/MullvadVPNUITests/Pages/TunnelControlPage.swift
new file mode 100644
index 0000000000..8fbee875bf
--- /dev/null
+++ b/ios/MullvadVPNUITests/Pages/TunnelControlPage.swift
@@ -0,0 +1,28 @@
+//
+// TunnelControlPage.swift
+// MullvadVPNUITests
+//
+// Created by Niklas Berglund on 2024-01-11.
+// Copyright © 2024 Mullvad VPN AB. All rights reserved.
+//
+
+import Foundation
+import XCTest
+
+class TunnelControlPage: Page {
+ @discardableResult override init(_ app: XCUIApplication) {
+ super.init(app)
+
+ self.pageAccessibilityIdentifier = .tunnelControlView
+ }
+
+ @discardableResult func tapSelectLocationButton() -> Self {
+ 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
new file mode 100644
index 0000000000..45d3477e2c
--- /dev/null
+++ b/ios/MullvadVPNUITests/RelayTests.swift
@@ -0,0 +1,96 @@
+//
+// RelayTests.swift
+// MullvadVPNUITests
+//
+// Created by Niklas Berglund on 2024-01-11.
+// Copyright © 2024 Mullvad VPN AB. All rights reserved.
+//
+
+import Foundation
+import XCTest
+
+class RelayTests: BaseUITestCase {
+ 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()
+
+ SelectLocationPage(app)
+ .tapLocationCellExpandButton(withName: "Sweden")
+ .tapLocationCellExpandButton(withName: "Gothenburg")
+ .tapLocationCell(withName: "se-got-wg-001")
+
+ allowAddVPNConfigurations() // Allow adding VPN configurations iOS permission
+
+ TunnelControlPage(app) // Make sure we're taken back to tunnel control page again
+
+ verifyCanReachAdServingDomain()
+
+ TunnelControlPage(app)
+ .tapSettingsButton()
+
+ SettingsPage(app)
+ .tapVPNSettingsCell()
+ .tapDNSSettingsCell()
+ .tapDNSContentBlockingHeaderExpandButton()
+ .tapBlockAdsSwitch()
+
+ verifyCannotReachAdServingDomain()
+ }
+
+ /// Verify that an ad serving domain is reachable by making sure the host can be found when sending HTTP request to it
+ func verifyCanReachAdServingDomain() {
+ XCTAssertTrue(canReachAdServingDomain())
+ }
+
+ /// Verify that an ad serving domain is NOT reachable by making sure the host cannot be found when sending HTTP request to it
+ func verifyCannotReachAdServingDomain() {
+ XCTAssertFalse(canReachAdServingDomain())
+ }
+
+ /// Attempt to reach HTTP server on an ad serving domain
+ /// - Returns: `true` if host can be resolved, otherwise `false`
+ private func canReachAdServingDomain() -> Bool {
+ guard let url = URL(string: "http://\(adServingDomain)") else { return false }
+
+ var requestError: Error?
+ var requestResponse: URLResponse?
+
+ let completionHandlerInvokedExpectation = expectation(
+ description: "Completion handler for the request is invoked"
+ )
+
+ let task = URLSession.shared.dataTask(with: url) { _, response, error in
+ requestError = error
+ requestResponse = response
+ completionHandlerInvokedExpectation.fulfill()
+ }
+
+ task.resume()
+
+ wait(for: [completionHandlerInvokedExpectation], timeout: 30)
+
+ if let urlError = requestError as? URLError {
+ if urlError.code == .cannotFindHost && requestResponse == nil {
+ return false
+ }
+ }
+
+ return true
+ }
+}
diff --git a/ios/MullvadVPNUITests/XCUIElementQuery+Extensions.swift b/ios/MullvadVPNUITests/XCUIElementQuery+Extensions.swift
new file mode 100644
index 0000000000..8500d7c08c
--- /dev/null
+++ b/ios/MullvadVPNUITests/XCUIElementQuery+Extensions.swift
@@ -0,0 +1,16 @@
+//
+// XCUIElementQuery+Extensions.swift
+// MullvadVPNUITests
+//
+// Created by Niklas Berglund on 2024-01-19.
+// Copyright © 2024 Mullvad VPN AB. All rights reserved.
+//
+
+import Foundation
+import XCTest
+
+extension XCUIElementQuery {
+ subscript(key: any RawRepresentable<String>) -> XCUIElement {
+ self[key.rawValue]
+ }
+}