summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorNiklas Berglund <niklas.berglund@gmail.com>2024-01-30 12:33:53 +0100
committerBug Magnet <marco.nikic@mullvad.net>2024-03-11 15:45:50 +0100
commit61ad690fcf058a82d7a016e50ffe289836ae28b2 (patch)
treeba4e75a285ce581678de38c3c5d00d589d336279
parent1c64c5ff5f300e9c02913c652e6121a47f783755 (diff)
downloadmullvadvpn-61ad690fcf058a82d7a016e50ffe289836ae28b2.tar.xz
mullvadvpn-61ad690fcf058a82d7a016e50ffe289836ae28b2.zip
Add account creation and deletion tests
-rw-r--r--ios/MullvadVPN.xcodeproj/project.pbxproj61
-rw-r--r--ios/MullvadVPN.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved22
-rw-r--r--ios/MullvadVPN/Classes/AccessbilityIdentifier.swift9
-rw-r--r--ios/MullvadVPN/Coordinators/ChangeLogCoordinator.swift1
-rw-r--r--ios/MullvadVPN/View controllers/AccountDeletion/AccountDeletionContentView.swift2
-rw-r--r--ios/MullvadVPN/View controllers/Alert/AlertPresentation.swift1
-rw-r--r--ios/MullvadVPN/View controllers/Alert/AlertViewController.swift5
-rw-r--r--ios/MullvadVPN/View controllers/CreationAccount/Welcome/WelcomeContentView.swift2
-rw-r--r--ios/MullvadVPN/View controllers/Login/LoginContentView.swift1
-rw-r--r--ios/MullvadVPN/View controllers/OutOfTime/OutOfTimeContentView.swift1
-rw-r--r--ios/MullvadVPN/View controllers/RevokedDevice/RevokedDeviceViewController.swift2
-rw-r--r--ios/MullvadVPNUITests/AccountTests.swift46
-rw-r--r--ios/MullvadVPNUITests/MullvadApi.swift4
-rw-r--r--ios/MullvadVPNUITests/Networking/MullvadAPIWrapper.swift65
-rw-r--r--ios/MullvadVPNUITests/Pages/AccountDeletionPage.swift39
-rw-r--r--ios/MullvadVPNUITests/Pages/ChangeLogAlert.swift24
-rw-r--r--ios/MullvadVPNUITests/Pages/HeaderBar.swift7
-rw-r--r--ios/MullvadVPNUITests/Pages/LoginPage.swift5
-rw-r--r--ios/MullvadVPNUITests/Pages/OutOfTimePage.swift19
-rw-r--r--ios/MullvadVPNUITests/Pages/RevokedDevicePage.swift26
-rw-r--r--ios/MullvadVPNUITests/Pages/SelectLocationPage.swift1
-rw-r--r--ios/MullvadVPNUITests/Pages/SettingsPage.swift1
-rw-r--r--ios/MullvadVPNUITests/Pages/TermsOfServicePage.swift1
-rw-r--r--ios/MullvadVPNUITests/Pages/TunnelControlPage.swift1
-rw-r--r--ios/MullvadVPNUITests/Pages/WelcomePage.swift34
-rw-r--r--ios/MullvadVPNUITests/README.md4
-rw-r--r--ios/MullvadVPNUITests/Test base classes/BaseUITestCase.swift26
-rw-r--r--ios/MullvadVPNUITests/Test base classes/LoggedInWithTimeUITestCase.swift1
-rw-r--r--ios/MullvadVPNUITests/Test base classes/LoggedInWithoutTimeUITestCase.swift1
-rw-r--r--ios/MullvadVPNUITests/Test base classes/LoggedOutUITestCase.swift1
-rw-r--r--ios/TestPlans/MullvadVPNUITestsAll.xctestplan8
-rw-r--r--mullvad-api/src/ios_ffi.rs374
-rw-r--r--mullvad-api/src/lib.rs21
33 files changed, 385 insertions, 431 deletions
diff --git a/ios/MullvadVPN.xcodeproj/project.pbxproj b/ios/MullvadVPN.xcodeproj/project.pbxproj
index 3c291ce3b2..815b5a268f 100644
--- a/ios/MullvadVPN.xcodeproj/project.pbxproj
+++ b/ios/MullvadVPN.xcodeproj/project.pbxproj
@@ -7,7 +7,6 @@
objects = {
/* Begin PBXBuildFile section */
- 01EF6F2A2B6A473900125696 /* MullvadApi.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01EF6F292B6A473900125696 /* MullvadApi.swift */; };
01EF6F342B6A590700125696 /* libmullvad_api.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 01EF6F332B6A590700125696 /* libmullvad_api.a */; };
062B45A328FD4CA700746E77 /* le_root_cert.cer in Resources */ = {isa = PBXBuildFile; fileRef = 06799AB428F98CE700ACD94E /* le_root_cert.cer */; };
062B45BC28FD8C3B00746E77 /* RESTDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = 062B45BB28FD8C3B00746E77 /* RESTDefaults.swift */; };
@@ -608,6 +607,7 @@
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 */; };
+ 85139B2D2B84B4A700734217 /* OutOfTimePage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85139B2C2B84B4A700734217 /* OutOfTimePage.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 */; };
@@ -618,16 +618,23 @@
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 */; };
+ 8556EB522B9A1C6900D26DD4 /* MullvadApi.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8556EB512B9A1C6900D26DD4 /* MullvadApi.swift */; };
+ 8556EB542B9A1D7100D26DD4 /* BridgingHeader.h in Headers */ = {isa = PBXBuildFile; fileRef = 8556EB532B9A1D7100D26DD4 /* BridgingHeader.h */; };
+ 8556EB562B9B0AC500D26DD4 /* RevokedDevicePage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8556EB552B9B0AC500D26DD4 /* RevokedDevicePage.swift */; };
855D9F5B2B63E56B00D7C64D /* ProblemReportPage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 855D9F5A2B63E56B00D7C64D /* ProblemReportPage.swift */; };
+ 8587A05D2B84D43100152938 /* ChangeLogAlert.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8587A05C2B84D43100152938 /* ChangeLogAlert.swift */; };
8590896C2B61763B003AF5F5 /* LoggedInWithoutTimeUITestCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 859089682B61763B003AF5F5 /* LoggedInWithoutTimeUITestCase.swift */; };
8590896F2B61763B003AF5F5 /* LoggedOutUITestCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8590896B2B61763B003AF5F5 /* LoggedOutUITestCase.swift */; };
+ 85B267612B849ADB0098E3CD /* mullvad-api.h in Headers */ = {isa = PBXBuildFile; fileRef = 85B267602B849ADB0098E3CD /* mullvad-api.h */; };
85C7A2E92B89024B00035D5A /* SettingsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85C7A2E82B89024B00035D5A /* SettingsTests.swift */; };
85D2B0B12B6BD32400DF9DA7 /* BaseUITestCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8590896A2B61763B003AF5F5 /* BaseUITestCase.swift */; };
85E3BDE52B70E18C00FA71FD /* Networking.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85E3BDE42B70E18C00FA71FD /* Networking.swift */; };
+ 85EC620C2B838D10005AFFB5 /* MullvadAPIWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85557B132B5983CF00795FE1 /* MullvadAPIWrapper.swift */; };
+ 85FB5A0C2B6903990015DCED /* WelcomePage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85FB5A0B2B6903990015DCED /* WelcomePage.swift */; };
+ 85FB5A102B6960A30015DCED /* AccountDeletionPage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85FB5A0F2B6960A30015DCED /* AccountDeletionPage.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 */; };
@@ -787,7 +794,6 @@
A9C342C52ACC42130045F00E /* ServerRelaysResponse+Stubs.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9C342C42ACC42130045F00E /* ServerRelaysResponse+Stubs.swift */; };
A9D99B9A2A1F7C3200DE27D3 /* RESTTransport.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06FAE67D28F83CA50033DD93 /* RESTTransport.swift */; };
A9DF789B2B7D1DF10094E4AD /* mullvad-api.h in Headers */ = {isa = PBXBuildFile; fileRef = 01EF6F2D2B6A51B100125696 /* mullvad-api.h */; settings = {ATTRIBUTES = (Private, ); }; };
- A9DF789C2B7D1E410094E4AD /* BridgingHeader.h in Headers */ = {isa = PBXBuildFile; fileRef = 01EF6F352B6A5AEF00125696 /* BridgingHeader.h */; settings = {ATTRIBUTES = (Public, ); }; };
A9DF789D2B7D1E8B0094E4AD /* LoggedInWithTimeUITestCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 859089692B61763B003AF5F5 /* LoggedInWithTimeUITestCase.swift */; };
A9E031782ACB09930095D843 /* UIApplication+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9E031762ACB08950095D843 /* UIApplication+Extensions.swift */; };
A9E0317A2ACB0AE70095D843 /* UIApplication+Stubs.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9E031792ACB0AE70095D843 /* UIApplication+Stubs.swift */; };
@@ -1250,12 +1256,10 @@
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
- 01EF6F292B6A473900125696 /* MullvadApi.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MullvadApi.swift; sourceTree = "<group>"; };
- 01EF6F2D2B6A51B100125696 /* mullvad-api.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "mullvad-api.h"; path = "../../mullvad-api/include/mullvad-api.h"; sourceTree = "<group>"; };
+ 01EF6F2D2B6A51B100125696 /* mullvad-api.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "mullvad-api.h"; path = "../mullvad-api/include/mullvad-api.h"; sourceTree = "<group>"; };
01EF6F2F2B6A588300125696 /* aarch64-apple-ios */ = {isa = PBXFileReference; lastKnownFileType = folder; name = "aarch64-apple-ios"; path = "../target/aarch64-apple-ios"; sourceTree = "<group>"; };
01EF6F312B6A58F000125696 /* debug */ = {isa = PBXFileReference; lastKnownFileType = folder; name = debug; path = "../target/aarch64-apple-ios/debug"; sourceTree = "<group>"; };
01EF6F332B6A590700125696 /* libmullvad_api.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libmullvad_api.a; path = "../target/aarch64-apple-ios/debug/libmullvad_api.a"; sourceTree = "<group>"; };
- 01EF6F352B6A5AEF00125696 /* BridgingHeader.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BridgingHeader.h; sourceTree = "<group>"; };
01F1FF1D29F0627D007083C3 /* libshadowsocks_proxy.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libshadowsocks_proxy.a; path = ../target/debug/libshadowsocks_proxy.a; sourceTree = "<group>"; };
062B45BB28FD8C3B00746E77 /* RESTDefaults.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RESTDefaults.swift; sourceTree = "<group>"; };
063687AF28EB083800BE7161 /* ProxyURLRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProxyURLRequest.swift; sourceTree = "<group>"; };
@@ -1835,6 +1839,7 @@
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>"; };
850201E22B51A93C00EF8C96 /* SettingsPage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsPage.swift; sourceTree = "<group>"; };
+ 85139B2C2B84B4A700734217 /* OutOfTimePage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OutOfTimePage.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>"; };
@@ -1853,13 +1858,20 @@
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>"; };
+ 8556EB512B9A1C6900D26DD4 /* MullvadApi.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = MullvadApi.swift; path = MullvadVPNUITests/MullvadApi.swift; sourceTree = "<group>"; };
+ 8556EB532B9A1D7100D26DD4 /* BridgingHeader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BridgingHeader.h; sourceTree = "<group>"; };
+ 8556EB552B9B0AC500D26DD4 /* RevokedDevicePage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RevokedDevicePage.swift; sourceTree = "<group>"; };
855D9F5A2B63E56B00D7C64D /* ProblemReportPage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProblemReportPage.swift; sourceTree = "<group>"; };
+ 8587A05C2B84D43100152938 /* ChangeLogAlert.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChangeLogAlert.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>"; };
+ 85B267602B849ADB0098E3CD /* mullvad-api.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "mullvad-api.h"; path = "../../mullvad-api/include/mullvad-api.h"; sourceTree = "<group>"; };
85C7A2E82B89024B00035D5A /* SettingsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsTests.swift; sourceTree = "<group>"; };
85E3BDE42B70E18C00FA71FD /* Networking.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Networking.swift; sourceTree = "<group>"; };
+ 85FB5A0B2B6903990015DCED /* WelcomePage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WelcomePage.swift; sourceTree = "<group>"; };
+ 85FB5A0F2B6960A30015DCED /* AccountDeletionPage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountDeletionPage.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>"; };
@@ -3102,6 +3114,7 @@
58CE5E57224146200008646E = {
isa = PBXGroup;
children = (
+ 8556EB512B9A1C6900D26DD4 /* MullvadApi.swift */,
01EF6F2D2B6A51B100125696 /* mullvad-api.h */,
58F3C0A824A50C0E003E76BE /* Assets */,
58ECD29023F178FD004298B6 /* Configurations */,
@@ -3545,12 +3558,11 @@
852969262B4D9C1F007EAD4C /* MullvadVPNUITests */ = {
isa = PBXGroup;
children = (
+ 85B267602B849ADB0098E3CD /* mullvad-api.h */,
852969272B4D9C1F007EAD4C /* AccountTests.swift */,
+ 8556EB532B9A1D7100D26DD4 /* BridgingHeader.h */,
85557B112B594FC900795FE1 /* ConnectivityTests.swift */,
- 01EF6F352B6A5AEF00125696 /* BridgingHeader.h */,
852969372B4ED20E007EAD4C /* Info.plist */,
- 01EF6F2D2B6A51B100125696 /* mullvad-api.h */,
- 01EF6F292B6A473900125696 /* MullvadApi.swift */,
85557B0C2B591B0F00795FE1 /* Networking */,
852969312B4E9220007EAD4C /* Pages */,
850201DA2B503D7700EF8C96 /* RelayTests.swift */,
@@ -3564,17 +3576,22 @@
852969312B4E9220007EAD4C /* Pages */ = {
isa = PBXGroup;
children = (
+ 85FB5A0F2B6960A30015DCED /* AccountDeletionPage.swift */,
85557B1F2B5FBBD700795FE1 /* AccountPage.swift */,
8529693B2B4F0257007EAD4C /* Alert.swift */,
+ 8587A05C2B84D43100152938 /* ChangeLogAlert.swift */,
85557B1D2B5FB8C700795FE1 /* HeaderBar.swift */,
852969342B4E9270007EAD4C /* LoginPage.swift */,
+ 85139B2C2B84B4A700734217 /* OutOfTimePage.swift */,
852969322B4E9232007EAD4C /* Page.swift */,
855D9F5A2B63E56B00D7C64D /* ProblemReportPage.swift */,
8532E6862B8CCED600ACECD1 /* ProblemReportSubmittedPage.swift */,
+ 8556EB552B9B0AC500D26DD4 /* RevokedDevicePage.swift */,
850201DC2B503D8C00EF8C96 /* SelectLocationPage.swift */,
850201E22B51A93C00EF8C96 /* SettingsPage.swift */,
852969392B4F0238007EAD4C /* TermsOfServicePage.swift */,
850201DE2B5040A500EF8C96 /* TunnelControlPage.swift */,
+ 85FB5A0B2B6903990015DCED /* WelcomePage.swift */,
);
path = Pages;
sourceTree = "<group>";
@@ -3788,7 +3805,8 @@
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
- A9DF789C2B7D1E410094E4AD /* BridgingHeader.h in Headers */,
+ 85B267612B849ADB0098E3CD /* mullvad-api.h in Headers */,
+ 8556EB542B9A1D7100D26DD4 /* BridgingHeader.h in Headers */,
A9DF789B2B7D1DF10094E4AD /* mullvad-api.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
@@ -4272,6 +4290,8 @@
8529692C2B4D9C1F007EAD4C /* PBXTargetDependency */,
);
name = MullvadVPNUITests;
+ packageProductDependencies = (
+ );
productName = MullvadVPNUITests;
productReference = 852969252B4D9C1F007EAD4C /* MullvadVPNUITests.xctest */;
productType = "com.apple.product-type.bundle.ui-testing";
@@ -5527,12 +5547,14 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
+ 8556EB522B9A1C6900D26DD4 /* MullvadApi.swift in Sources */,
+ 85EC620C2B838D10005AFFB5 /* MullvadAPIWrapper.swift in Sources */,
A9DF789D2B7D1E8B0094E4AD /* LoggedInWithTimeUITestCase.swift in Sources */,
85D2B0B12B6BD32400DF9DA7 /* BaseUITestCase.swift in Sources */,
8529693C2B4F0257007EAD4C /* Alert.swift in Sources */,
850201DD2B503D8C00EF8C96 /* SelectLocationPage.swift in Sources */,
850201DB2B503D7700EF8C96 /* RelayTests.swift in Sources */,
- 85557B142B5983CF00795FE1 /* MullvadAPIWrapper.swift in Sources */,
+ 85139B2D2B84B4A700734217 /* OutOfTimePage.swift in Sources */,
852969362B4E9724007EAD4C /* AccessbilityIdentifier.swift in Sources */,
85E3BDE52B70E18C00FA71FD /* Networking.swift in Sources */,
85C7A2E92B89024B00035D5A /* SettingsTests.swift in Sources */,
@@ -5540,14 +5562,17 @@
8590896F2B61763B003AF5F5 /* LoggedOutUITestCase.swift in Sources */,
85557B202B5FBBD700795FE1 /* AccountPage.swift in Sources */,
852969352B4E9270007EAD4C /* LoginPage.swift in Sources */,
+ 8556EB562B9B0AC500D26DD4 /* RevokedDevicePage.swift in Sources */,
85557B102B59215F00795FE1 /* FirewallRule.swift in Sources */,
85557B0E2B591B2600795FE1 /* FirewallAPIClient.swift in Sources */,
852969282B4D9C1F007EAD4C /* AccountTests.swift in Sources */,
- 01EF6F2A2B6A473900125696 /* MullvadApi.swift in Sources */,
+ 8587A05D2B84D43100152938 /* ChangeLogAlert.swift in Sources */,
+ 85FB5A102B6960A30015DCED /* AccountDeletionPage.swift in Sources */,
85557B162B5ABBBE00795FE1 /* XCUIElementQuery+Extensions.swift in Sources */,
855D9F5B2B63E56B00D7C64D /* ProblemReportPage.swift in Sources */,
8529693A2B4F0238007EAD4C /* TermsOfServicePage.swift in Sources */,
8532E6872B8CCED600ACECD1 /* ProblemReportSubmittedPage.swift in Sources */,
+ 85FB5A0C2B6903990015DCED /* WelcomePage.swift in Sources */,
850201DF2B5040A500EF8C96 /* TunnelControlPage.swift in Sources */,
85557B1E2B5FB8C700795FE1 /* HeaderBar.swift in Sources */,
85557B122B594FC900795FE1 /* ConnectivityTests.swift in Sources */,
@@ -6340,6 +6365,7 @@
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_NO_COMMON_BLOCKS = YES;
+ GCC_PREPROCESSOR_DEFINITIONS = "";
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
@@ -6904,8 +6930,12 @@
"DEVELOPMENT_TEAM[sdk=macosx*]" = CKG9MXH72F;
ENABLE_USER_SCRIPT_SANDBOXING = NO;
GCC_C_LANGUAGE_STANDARD = gnu17;
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "DEBUG=1",
+ "$(inherited)",
+ );
GENERATE_INFOPLIST_FILE = YES;
- HEADER_SEARCH_PATHS = "$(PROJECT_DIR)/../mullvad-api/include";
+ HEADER_SEARCH_PATHS = "$(PROJECT_DIR)/mullvad-api/include";
INFOPLIST_FILE = MullvadVPNUITests/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 14.2;
"LIBRARY_SEARCH_PATHS[sdk=iphoneos*][arch=arm64]" = "$(PROJECT_DIR)/../target/aarch64-apple-ios/debug";
@@ -6938,7 +6968,7 @@
ENABLE_USER_SCRIPT_SANDBOXING = NO;
GCC_C_LANGUAGE_STANDARD = gnu17;
GENERATE_INFOPLIST_FILE = YES;
- HEADER_SEARCH_PATHS = "$(PROJECT_DIR)/../mullvad-api/include";
+ HEADER_SEARCH_PATHS = "$(PROJECT_DIR)/mullvad-api/include";
INFOPLIST_FILE = MullvadVPNUITests/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 14.2;
"LIBRARY_SEARCH_PATHS[sdk=iphoneos*][arch=arm64]" = "$(PROJECT_DIR)/../target/aarch64-apple-ios/release";
@@ -7561,11 +7591,14 @@
baseConfigurationReference = 852969382B4ED818007EAD4C /* UITests.xcconfig */;
buildSettings = {
APPLICATION_IDENTIFIER = net.mullvad.MullvadVPN;
+ GCC_PREPROCESSOR_DEFINITIONS = "";
+ GENERATE_INFOPLIST_FILE = YES;
HEADER_SEARCH_PATHS = "$(PROJECT_DIR)/../mullvad-api/include";
INFOPLIST_FILE = MullvadVPNUITests/Info.plist;
"LIBRARY_SEARCH_PATHS[sdk=iphoneos*][arch=arm64]" = "$(PROJECT_DIR)/../target/aarch64-apple-ios/release";
"LIBRARY_SEARCH_PATHS[sdk=iphonesimulator*][arch=arm64]" = "$(PROJECT_DIR)/../target/aarch64-apple-ios-sim/release";
"LIBRARY_SEARCH_PATHS[sdk=iphonesimulator*][arch=x86_64]" = "$(PROJECT_DIR)/../target/x86_64-apple-ios/release";
+ PRODUCT_BUNDLE_IDENTIFIER = "$(APPLICATION_IDENTIFIER).MullvadVPNUITests";
PRODUCT_NAME = MullvadVPNUITests;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = MULLVAD_ENVIRONMENT_PRODUCTION;
SWIFT_OBJC_BRIDGING_HEADER = "$(PROJECT_DIR)/MullvadVPNUITests/BridgingHeader.h";
diff --git a/ios/MullvadVPN.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/ios/MullvadVPN.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved
new file mode 100644
index 0000000000..02691892fe
--- /dev/null
+++ b/ios/MullvadVPN.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved
@@ -0,0 +1,22 @@
+{
+ "pins" : [
+ {
+ "identity" : "swift-log",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/apple/swift-log.git",
+ "state" : {
+ "revision" : "173f567a2dfec11d74588eea82cecea555bdc0bc",
+ "version" : "1.4.0"
+ }
+ },
+ {
+ "identity" : "wireguard-apple",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/mullvad/wireguard-apple.git",
+ "state" : {
+ "revision" : "11a00c20dc03f2751db47e94f585c0778c7bde82"
+ }
+ }
+ ],
+ "version" : 2
+}
diff --git a/ios/MullvadVPN/Classes/AccessbilityIdentifier.swift b/ios/MullvadVPN/Classes/AccessbilityIdentifier.swift
index a5dc7f26a5..8a911b97c1 100644
--- a/ios/MullvadVPN/Classes/AccessbilityIdentifier.swift
+++ b/ios/MullvadVPN/Classes/AccessbilityIdentifier.swift
@@ -17,8 +17,10 @@ public enum AccessibilityIdentifier: String {
case cancelButton
case connectionPanelButton
case collapseButton
+ case createAccountButton
case deleteButton
case disconnectButton
+ case revokedDeviceLoginButton
case infoButton
case learnAboutPrivacyButton
case loginBarButton
@@ -47,13 +49,16 @@ public enum AccessibilityIdentifier: String {
// Labels
case headerDeviceNameLabel
case connectionStatusLabel
+ case welcomeAccountNumberLabel
// Views
case accountView
case alertContainerView
case alertTitle
+ case changeLogAlert
case headerBarView
case loginView
+ case outOfTimeView
case termsOfServiceView
case selectLocationView
case selectLocationTableView
@@ -61,6 +66,9 @@ public enum AccessibilityIdentifier: String {
case tunnelControlView
case problemReportView
case problemReportSubmittedView
+ case revokedDeviceView
+ case welcomeView
+ case deleteAccountView
// Other UI elements
case connectionPanelInAddressRow
@@ -71,6 +79,7 @@ public enum AccessibilityIdentifier: String {
case selectLocationSearchTextField
case problemReportEmailTextField
case problemReportMessageTextView
+ case deleteAccountTextField
// DNS settings
case dnsSettings
diff --git a/ios/MullvadVPN/Coordinators/ChangeLogCoordinator.swift b/ios/MullvadVPN/Coordinators/ChangeLogCoordinator.swift
index c3e53cc9f4..031ba6a972 100644
--- a/ios/MullvadVPN/Coordinators/ChangeLogCoordinator.swift
+++ b/ios/MullvadVPN/Coordinators/ChangeLogCoordinator.swift
@@ -26,6 +26,7 @@ final class ChangeLogCoordinator: Coordinator, Presentable {
func start() {
let presentation = AlertPresentation(
id: "change-log-ok-alert",
+ accessibilityIdentifier: .changeLogAlert,
header: interactor.viewModel.header,
title: interactor.viewModel.title,
attributedMessage: interactor.viewModel.body,
diff --git a/ios/MullvadVPN/View controllers/AccountDeletion/AccountDeletionContentView.swift b/ios/MullvadVPN/View controllers/AccountDeletion/AccountDeletionContentView.swift
index 1929b47dd9..f5f0bb2aa8 100644
--- a/ios/MullvadVPN/View controllers/AccountDeletion/AccountDeletionContentView.swift
+++ b/ios/MullvadVPN/View controllers/AccountDeletion/AccountDeletionContentView.swift
@@ -81,6 +81,7 @@ class AccountDeletionContentView: UIView {
private lazy var accountTextField: AccountTextField = {
let groupingStyle = AccountTextField.GroupingStyle.lastPart
let textField = AccountTextField(groupingStyle: groupingStyle)
+ textField.accessibilityIdentifier = .deleteAccountTextField
textField.font = .preferredFont(forTextStyle: .body, weight: .bold)
textField.placeholder = Array(repeating: "X", count: 4).joined()
textField.placeholderTextColor = .lightGray
@@ -346,6 +347,7 @@ class AccountDeletionContentView: UIView {
}
private func setupAppearance() {
+ accessibilityIdentifier = .deleteAccountView
translatesAutoresizingMaskIntoConstraints = false
backgroundColor = .secondaryColor
directionalLayoutMargins = UIMetrics.contentLayoutMargins
diff --git a/ios/MullvadVPN/View controllers/Alert/AlertPresentation.swift b/ios/MullvadVPN/View controllers/Alert/AlertPresentation.swift
index b0056b7ea9..afd67b6e81 100644
--- a/ios/MullvadVPN/View controllers/Alert/AlertPresentation.swift
+++ b/ios/MullvadVPN/View controllers/Alert/AlertPresentation.swift
@@ -24,6 +24,7 @@ struct AlertAction {
struct AlertPresentation: Identifiable, CustomDebugStringConvertible {
let id: String
+ var accessibilityIdentifier: AccessibilityIdentifier?
var header: String?
var icon: AlertIcon?
var title: String?
diff --git a/ios/MullvadVPN/View controllers/Alert/AlertViewController.swift b/ios/MullvadVPN/View controllers/Alert/AlertViewController.swift
index a116e51839..bfdb60c70c 100644
--- a/ios/MullvadVPN/View controllers/Alert/AlertViewController.swift
+++ b/ios/MullvadVPN/View controllers/Alert/AlertViewController.swift
@@ -56,8 +56,6 @@ class AlertViewController: UIViewController {
view.backgroundColor = .secondaryColor
view.layer.cornerRadius = 11
- view.accessibilityIdentifier = .alertContainerView
-
return view
}()
@@ -112,6 +110,9 @@ class AlertViewController: UIViewController {
view.backgroundColor = .black.withAlphaComponent(0.5)
+ let accessibilityIdentifier = presentation.accessibilityIdentifier ?? .alertContainerView
+ view.accessibilityIdentifier = accessibilityIdentifier
+
setContent()
setConstraints()
}
diff --git a/ios/MullvadVPN/View controllers/CreationAccount/Welcome/WelcomeContentView.swift b/ios/MullvadVPN/View controllers/CreationAccount/Welcome/WelcomeContentView.swift
index 466a6b24ad..4e055ad2f3 100644
--- a/ios/MullvadVPN/View controllers/CreationAccount/Welcome/WelcomeContentView.swift
+++ b/ios/MullvadVPN/View controllers/CreationAccount/Welcome/WelcomeContentView.swift
@@ -54,6 +54,7 @@ final class WelcomeContentView: UIView {
private let accountNumberLabel: UILabel = {
let label = UILabel()
+ label.accessibilityIdentifier = .welcomeAccountNumberLabel
label.adjustsFontForContentSizeCategory = true
label.lineBreakMode = .byWordWrapping
label.numberOfLines = .zero
@@ -192,6 +193,7 @@ final class WelcomeContentView: UIView {
override init(frame: CGRect) {
super.init(frame: frame)
+ accessibilityIdentifier = .welcomeView
backgroundColor = .primaryColor
directionalLayoutMargins = UIMetrics.contentLayoutMargins
backgroundColor = .secondaryColor
diff --git a/ios/MullvadVPN/View controllers/Login/LoginContentView.swift b/ios/MullvadVPN/View controllers/Login/LoginContentView.swift
index c994286d51..5e1bfec865 100644
--- a/ios/MullvadVPN/View controllers/Login/LoginContentView.swift
+++ b/ios/MullvadVPN/View controllers/Login/LoginContentView.swift
@@ -84,6 +84,7 @@ class LoginContentView: UIView {
let createAccountButton: AppButton = {
let button = AppButton(style: .default)
+ button.accessibilityIdentifier = .createAccountButton
button.translatesAutoresizingMaskIntoConstraints = false
button.setTitle(NSLocalizedString(
"CREATE_ACCOUNT_BUTTON_LABEL",
diff --git a/ios/MullvadVPN/View controllers/OutOfTime/OutOfTimeContentView.swift b/ios/MullvadVPN/View controllers/OutOfTime/OutOfTimeContentView.swift
index edd3f50105..843fd0cda4 100644
--- a/ios/MullvadVPN/View controllers/OutOfTime/OutOfTimeContentView.swift
+++ b/ios/MullvadVPN/View controllers/OutOfTime/OutOfTimeContentView.swift
@@ -95,6 +95,7 @@ class OutOfTimeContentView: UIView {
override init(frame: CGRect) {
super.init(frame: frame)
+ accessibilityIdentifier = .outOfTimeView
translatesAutoresizingMaskIntoConstraints = false
backgroundColor = .secondaryColor
directionalLayoutMargins = UIMetrics.contentLayoutMargins
diff --git a/ios/MullvadVPN/View controllers/RevokedDevice/RevokedDeviceViewController.swift b/ios/MullvadVPN/View controllers/RevokedDevice/RevokedDeviceViewController.swift
index d4dc37616d..ea80ff14ef 100644
--- a/ios/MullvadVPN/View controllers/RevokedDevice/RevokedDeviceViewController.swift
+++ b/ios/MullvadVPN/View controllers/RevokedDevice/RevokedDeviceViewController.swift
@@ -62,6 +62,7 @@ class RevokedDeviceViewController: UIViewController, RootContainment {
private lazy var logoutButton: AppButton = {
let button = AppButton(style: .default)
+ button.accessibilityIdentifier = .revokedDeviceLoginButton
button.translatesAutoresizingMaskIntoConstraints = false
button.setTitle(
NSLocalizedString(
@@ -108,6 +109,7 @@ class RevokedDeviceViewController: UIViewController, RootContainment {
override func viewDidLoad() {
super.viewDidLoad()
+ view.accessibilityIdentifier = .revokedDeviceView
view.backgroundColor = .secondaryColor
view.directionalLayoutMargins = UIMetrics.contentLayoutMargins
diff --git a/ios/MullvadVPNUITests/AccountTests.swift b/ios/MullvadVPNUITests/AccountTests.swift
index f15195b69d..e12152291b 100644
--- a/ios/MullvadVPNUITests/AccountTests.swift
+++ b/ios/MullvadVPNUITests/AccountTests.swift
@@ -9,6 +9,52 @@
import XCTest
class AccountTests: LoggedOutUITestCase {
+ override func setUpWithError() throws {
+ continueAfterFailure = false
+
+ try super.setUpWithError()
+ }
+
+ override func tearDownWithError() throws {}
+
+ func testCreateAccount() throws {
+ LoginPage(app)
+ .tapCreateAccountButton()
+
+ // Verify welcome page is shown and get account number from it
+ let accountNumber = WelcomePage(app).getAccountNumber()
+
+ try MullvadAPIWrapper().deleteAccount(accountNumber)
+ }
+
+ func testDeleteAccount() throws {
+ let accountNumber = try MullvadAPIWrapper().createAccount()
+
+ LoginPage(app)
+ .tapAccountNumberTextField()
+ .enterText(accountNumber)
+ .tapAccountNumberSubmitButton()
+
+ OutOfTimePage(app)
+
+ HeaderBar(app)
+ .tapAccountButton()
+
+ AccountPage(app)
+ .tapDeleteAccountButton()
+
+ AccountDeletionPage(app)
+ .enterText(String(accountNumber.suffix(4)))
+ .tapDeleteAccountButton()
+
+ // Attempt to login with deleted account and verify that it fails
+ LoginPage(app)
+ .tapAccountNumberTextField()
+ .enterText(accountNumber)
+ .tapAccountNumberSubmitButton()
+ .verifyFailIconShown()
+ }
+
func testLogin() throws {
LoginPage(app)
.tapAccountNumberTextField()
diff --git a/ios/MullvadVPNUITests/MullvadApi.swift b/ios/MullvadVPNUITests/MullvadApi.swift
index 3c23f8209d..a1a22b5c40 100644
--- a/ios/MullvadVPNUITests/MullvadApi.swift
+++ b/ios/MullvadVPNUITests/MullvadApi.swift
@@ -38,6 +38,10 @@ struct InitMutableBufferError: Error {
class MullvadApi {
private var clientContext = MullvadApiClient()
+ /// Initialize the Mullvad API client
+ /// - Parameters:
+ /// - apiAddress: Address of the Mullvad API server in the format \<IP-address\>:\<port\>
+ /// - hostname: Hostname of the Mullvad API server
init(apiAddress: String, hostname: String) throws {
let result = mullvad_api_client_initialize(
&clientContext,
diff --git a/ios/MullvadVPNUITests/Networking/MullvadAPIWrapper.swift b/ios/MullvadVPNUITests/Networking/MullvadAPIWrapper.swift
index 71343dd4f3..ba870b0ba7 100644
--- a/ios/MullvadVPNUITests/Networking/MullvadAPIWrapper.swift
+++ b/ios/MullvadVPNUITests/Networking/MullvadAPIWrapper.swift
@@ -1,5 +1,5 @@
//
-// AppAPI.swift
+// MullvadAPIWrapper.swift
// MullvadVPNUITests
//
// Created by Niklas Berglund on 2024-01-18.
@@ -10,7 +10,8 @@ import Foundation
import XCTest
enum MullvadAPIError: Error {
- case incorrectConfigurationFormat
+ case invalidEndpointFormatError
+ case requestError
}
class MullvadAPIWrapper {
@@ -18,18 +19,22 @@ class MullvadAPIWrapper {
static let hostName = Bundle(for: MullvadAPIWrapper.self)
.infoDictionary?["ApiHostName"] as! String
+ private var mullvadAPI: MullvadApi
+
/// API endpoint configuration value in the format <IP-address>:<port>
static let endpoint = Bundle(for: MullvadAPIWrapper.self)
.infoDictionary?["ApiEndpoint"] as! String
// swiftlint:enable force_cast
- public static func getAPIHostname() -> String {
- return hostName
+ init() throws {
+ let apiAddress = try Self.getAPIIPAddress() + ":" + Self.getAPIPort()
+ let hostname = Self.hostName
+ mullvadAPI = try MullvadApi(apiAddress: apiAddress, hostname: hostname)
}
public static func getAPIIPAddress() throws -> String {
guard let ipAddress = endpoint.components(separatedBy: ":").first else {
- throw MullvadAPIError.incorrectConfigurationFormat
+ throw MullvadAPIError.invalidEndpointFormatError
}
return ipAddress
@@ -37,9 +42,57 @@ class MullvadAPIWrapper {
public static func getAPIPort() throws -> String {
guard let port = endpoint.components(separatedBy: ":").last else {
- throw MullvadAPIError.incorrectConfigurationFormat
+ throw MullvadAPIError.invalidEndpointFormatError
}
return port
}
+
+ /// Generate a mock WireGuard key
+ private func generateMockWireGuardKey() -> Data {
+ var bytes = [UInt8]()
+
+ for _ in 0 ..< 44 {
+ bytes.append(UInt8.random(in: 0 ..< 255))
+ }
+
+ return Data(bytes)
+ }
+
+ func createAccount() -> String {
+ do {
+ let accountNumber = try mullvadAPI.createAccount()
+ return accountNumber
+ } catch {
+ XCTFail("Failed to create account using app API")
+ return String()
+ }
+ }
+
+ func deleteAccount(_ accountNumber: String) {
+ do {
+ try mullvadAPI.delete(account: accountNumber)
+ } catch {
+ XCTFail("Failed to delete account using app API")
+ }
+ }
+
+ /// Add another device to specified account. A dummy WireGuard key will be generated.
+ func addDevice(_ account: String) throws {
+ let devicePublicKey = generateMockWireGuardKey()
+
+ do {
+ try mullvadAPI.addDevice(forAccount: account, publicKey: devicePublicKey)
+ } catch {
+ throw MullvadAPIError.requestError
+ }
+ }
+
+ func getAccountExpiry(_ account: String) throws -> UInt64 {
+ do {
+ return try mullvadAPI.getExpiry(forAccount: account)
+ } catch {
+ throw MullvadAPIError.requestError
+ }
+ }
}
diff --git a/ios/MullvadVPNUITests/Pages/AccountDeletionPage.swift b/ios/MullvadVPNUITests/Pages/AccountDeletionPage.swift
new file mode 100644
index 0000000000..30bf01034e
--- /dev/null
+++ b/ios/MullvadVPNUITests/Pages/AccountDeletionPage.swift
@@ -0,0 +1,39 @@
+//
+// AccountDeletionPage.swift
+// MullvadVPNUITests
+//
+// Created by Niklas Berglund on 2024-01-30.
+// Copyright © 2024 Mullvad VPN AB. All rights reserved.
+//
+
+import Foundation
+import XCTest
+
+class AccountDeletionPage: Page {
+ @discardableResult override init(_ app: XCUIApplication) {
+ super.init(app)
+
+ self.pageAccessibilityIdentifier = .deleteAccountView
+ waitForPageToBeShown()
+ }
+
+ @discardableResult func tapTextField() -> Self {
+ app.textFields[AccessibilityIdentifier.deleteAccountTextField].tap()
+ return self
+ }
+
+ @discardableResult func tapDeleteAccountButton() -> Self {
+ guard let pageAccessibilityIdentifier = self.pageAccessibilityIdentifier else {
+ XCTFail("Page's accessibility identifier not set")
+ return self
+ }
+
+ app.otherElements[pageAccessibilityIdentifier].buttons[AccessibilityIdentifier.deleteButton].tap()
+ return self
+ }
+
+ @discardableResult func tapCancelButton() -> Self {
+ app.buttons[AccessibilityIdentifier.cancelButton].tap()
+ return self
+ }
+}
diff --git a/ios/MullvadVPNUITests/Pages/ChangeLogAlert.swift b/ios/MullvadVPNUITests/Pages/ChangeLogAlert.swift
new file mode 100644
index 0000000000..70d67d0294
--- /dev/null
+++ b/ios/MullvadVPNUITests/Pages/ChangeLogAlert.swift
@@ -0,0 +1,24 @@
+//
+// ChangeLogAlert.swift
+// MullvadVPNUITests
+//
+// Created by Niklas Berglund on 2024-02-20.
+// Copyright © 2024 Mullvad VPN AB. All rights reserved.
+//
+
+import Foundation
+import XCTest
+
+class ChangeLogAlert: Page {
+ @discardableResult override init(_ app: XCUIApplication) {
+ super.init(app)
+
+ self.pageAccessibilityIdentifier = .changeLogAlert
+ waitForPageToBeShown()
+ }
+
+ @discardableResult func tapOkay() -> Self {
+ app.buttons[AccessibilityIdentifier.alertOkButton].tap()
+ return self
+ }
+}
diff --git a/ios/MullvadVPNUITests/Pages/HeaderBar.swift b/ios/MullvadVPNUITests/Pages/HeaderBar.swift
index cf0473af8c..129d721b37 100644
--- a/ios/MullvadVPNUITests/Pages/HeaderBar.swift
+++ b/ios/MullvadVPNUITests/Pages/HeaderBar.swift
@@ -10,6 +10,9 @@ import Foundation
import XCTest
class HeaderBar: Page {
+ lazy var accountButton = app.buttons[AccessibilityIdentifier.accountButton]
+ lazy var settingsButton = app.buttons[AccessibilityIdentifier.settingsButton]
+
@discardableResult override init(_ app: XCUIApplication) {
super.init(app)
@@ -18,12 +21,12 @@ class HeaderBar: Page {
}
@discardableResult func tapAccountButton() -> Self {
- app.buttons[AccessibilityIdentifier.accountButton.rawValue].tap()
+ accountButton.tap()
return self
}
@discardableResult func tapSettingsButton() -> Self {
- app.buttons[AccessibilityIdentifier.settingsButton.rawValue].tap()
+ settingsButton.tap()
return self
}
}
diff --git a/ios/MullvadVPNUITests/Pages/LoginPage.swift b/ios/MullvadVPNUITests/Pages/LoginPage.swift
index 399943fe52..96c6ebea89 100644
--- a/ios/MullvadVPNUITests/Pages/LoginPage.swift
+++ b/ios/MullvadVPNUITests/Pages/LoginPage.swift
@@ -27,6 +27,11 @@ class LoginPage: Page {
return self
}
+ @discardableResult public func tapCreateAccountButton() -> Self {
+ app.buttons[AccessibilityIdentifier.createAccountButton].tap()
+ return self
+ }
+
@discardableResult public func verifyDeviceLabelShown() -> Self {
XCTAssertTrue(
app.staticTexts[AccessibilityIdentifier.headerDeviceNameLabel]
diff --git a/ios/MullvadVPNUITests/Pages/OutOfTimePage.swift b/ios/MullvadVPNUITests/Pages/OutOfTimePage.swift
new file mode 100644
index 0000000000..0950aa9147
--- /dev/null
+++ b/ios/MullvadVPNUITests/Pages/OutOfTimePage.swift
@@ -0,0 +1,19 @@
+//
+// OutOfTimePage.swift
+// MullvadVPNUITests
+//
+// Created by Niklas Berglund on 2024-02-20.
+// Copyright © 2024 Mullvad VPN AB. All rights reserved.
+//
+
+import Foundation
+import XCTest
+
+class OutOfTimePage: Page {
+ @discardableResult override init(_ app: XCUIApplication) {
+ super.init(app)
+
+ self.pageAccessibilityIdentifier = .outOfTimeView
+ waitForPageToBeShown()
+ }
+}
diff --git a/ios/MullvadVPNUITests/Pages/RevokedDevicePage.swift b/ios/MullvadVPNUITests/Pages/RevokedDevicePage.swift
new file mode 100644
index 0000000000..4b8dd6dd72
--- /dev/null
+++ b/ios/MullvadVPNUITests/Pages/RevokedDevicePage.swift
@@ -0,0 +1,26 @@
+//
+// RevokedDevicePage.swift
+// MullvadVPNUITests
+//
+// Created by Niklas Berglund on 2024-03-08.
+// Copyright © 2024 Mullvad VPN AB. All rights reserved.
+//
+
+import Foundation
+import XCTest
+
+class RevokedDevicePage: Page {
+ @discardableResult override init(_ app: XCUIApplication) {
+ super.init(app)
+
+ self.pageAccessibilityIdentifier = .revokedDeviceView
+ waitForPageToBeShown()
+ }
+
+ @discardableResult func tapGoToLogin() -> Self {
+ app.buttons[AccessibilityIdentifier.revokedDeviceLoginButton]
+ .tap()
+
+ return self
+ }
+}
diff --git a/ios/MullvadVPNUITests/Pages/SelectLocationPage.swift b/ios/MullvadVPNUITests/Pages/SelectLocationPage.swift
index f841101ff6..c5c54325ad 100644
--- a/ios/MullvadVPNUITests/Pages/SelectLocationPage.swift
+++ b/ios/MullvadVPNUITests/Pages/SelectLocationPage.swift
@@ -14,6 +14,7 @@ class SelectLocationPage: Page {
super.init(app)
self.pageAccessibilityIdentifier = .selectLocationView
+ waitForPageToBeShown()
}
@discardableResult func tapLocationCell(withName name: String) -> Self {
diff --git a/ios/MullvadVPNUITests/Pages/SettingsPage.swift b/ios/MullvadVPNUITests/Pages/SettingsPage.swift
index 72acac4019..63b3227d29 100644
--- a/ios/MullvadVPNUITests/Pages/SettingsPage.swift
+++ b/ios/MullvadVPNUITests/Pages/SettingsPage.swift
@@ -14,6 +14,7 @@ class SettingsPage: Page {
super.init(app)
self.pageAccessibilityIdentifier = .settingsTableView
+ waitForPageToBeShown()
}
@discardableResult func tapVPNSettingsCell() -> Self {
diff --git a/ios/MullvadVPNUITests/Pages/TermsOfServicePage.swift b/ios/MullvadVPNUITests/Pages/TermsOfServicePage.swift
index b1a569290d..0f32bf7be6 100644
--- a/ios/MullvadVPNUITests/Pages/TermsOfServicePage.swift
+++ b/ios/MullvadVPNUITests/Pages/TermsOfServicePage.swift
@@ -14,6 +14,7 @@ class TermsOfServicePage: Page {
super.init(app)
self.pageAccessibilityIdentifier = .termsOfServiceView
+ waitForPageToBeShown()
}
@discardableResult func tapAgreeButton() -> Self {
diff --git a/ios/MullvadVPNUITests/Pages/TunnelControlPage.swift b/ios/MullvadVPNUITests/Pages/TunnelControlPage.swift
index 860b928024..37813b36c4 100644
--- a/ios/MullvadVPNUITests/Pages/TunnelControlPage.swift
+++ b/ios/MullvadVPNUITests/Pages/TunnelControlPage.swift
@@ -14,6 +14,7 @@ class TunnelControlPage: Page {
super.init(app)
self.pageAccessibilityIdentifier = .tunnelControlView
+ waitForPageToBeShown()
}
@discardableResult func tapSelectLocationButton() -> Self {
diff --git a/ios/MullvadVPNUITests/Pages/WelcomePage.swift b/ios/MullvadVPNUITests/Pages/WelcomePage.swift
new file mode 100644
index 0000000000..d6e8048413
--- /dev/null
+++ b/ios/MullvadVPNUITests/Pages/WelcomePage.swift
@@ -0,0 +1,34 @@
+//
+// WelcomePage.swift
+// MullvadVPNUITests
+//
+// Created by Niklas Berglund on 2024-01-30.
+// Copyright © 2024 Mullvad VPN AB. All rights reserved.
+//
+
+import Foundation
+import XCTest
+
+class WelcomePage: Page {
+ @discardableResult override init(_ app: XCUIApplication) {
+ super.init(app)
+
+ self.pageAccessibilityIdentifier = .welcomeView
+ waitForPageToBeShown()
+ }
+
+ @discardableResult func tapAddTimeButton() -> Self {
+ app.buttons[AccessibilityIdentifier.purchaseButton].tap()
+ return self
+ }
+
+ @discardableResult func tapRedeemButton() -> Self {
+ app.buttons[AccessibilityIdentifier.redeemVoucherButton].tap()
+ return self
+ }
+
+ func getAccountNumber() -> String {
+ let labelValue = app.staticTexts[AccessibilityIdentifier.welcomeAccountNumberLabel].label
+ return labelValue.replacingOccurrences(of: " ", with: "")
+ }
+}
diff --git a/ios/MullvadVPNUITests/README.md b/ios/MullvadVPNUITests/README.md
index 8602409322..6f2b4f0f08 100644
--- a/ios/MullvadVPNUITests/README.md
+++ b/ios/MullvadVPNUITests/README.md
@@ -25,10 +25,10 @@
- `brew install go@1.19`
## GitHub runner setup
-1. Ask GitHub admin for new runner token and set it up according to the steps presented, pass `--labels ios-test` to `config.sh` when running it. By default it will also have the labels `self-hosted` and `macOS` which are required as well.
+1. Ask GitHub admin for new runner token and setup steps from GitHub. Set it up according to the steps, pass `--labels ios-test` to `config.sh` when running it. By default it will also have the labels `self-hosted` and `macOS` which are required as well.
2. Make sure GitHub actions secrets for the repository are correctly set up:
- `IOS_DEVICE_PIN_CODE` - Device passcode if the device require it, otherwise leave blank. Devices used with CI should not require passcode.
- `IOS_HAS_TIME_ACCOUNT_NUMBER` - Production server account without time left
- `IOS_NO_TIME_ACCOUNT_NUMBER` - Production server account with time added to it
- `IOS_TEST_DEVICE_IDENTIFIER_UUID` - unique identifier for the test device. Create new identifier with `uuidgen`.
- - `IOS_TEST_DEVICE_UDID` - the iOS device's UDID. \ No newline at end of file
+ - `IOS_TEST_DEVICE_UDID` - the iOS device's UDID.
diff --git a/ios/MullvadVPNUITests/Test base classes/BaseUITestCase.swift b/ios/MullvadVPNUITests/Test base classes/BaseUITestCase.swift
index c99f25463e..2e4a252c76 100644
--- a/ios/MullvadVPNUITests/Test base classes/BaseUITestCase.swift
+++ b/ios/MullvadVPNUITests/Test base classes/BaseUITestCase.swift
@@ -76,11 +76,17 @@ class BaseUITestCase: XCTestCase {
if termsOfServiceIsShown {
TermsOfServicePage(app)
.tapAgreeButton()
+ }
+ }
- Alert(app) // Changes alert
- .tapOkay()
+ func dismissChangeLogIfShown() {
+ let changeLogIsShown = app
+ .otherElements[AccessibilityIdentifier.changeLogAlert.rawValue]
+ .waitForExistence(timeout: 1.0)
- LoginPage(app)
+ if changeLogIsShown {
+ ChangeLogAlert(app)
+ .tapOkay()
}
}
@@ -95,11 +101,17 @@ class BaseUITestCase: XCTestCase {
func logoutIfLoggedIn() {
if isLoggedIn() {
- HeaderBar(app)
- .tapAccountButton()
+ if app.buttons[AccessibilityIdentifier.accountButton].exists {
+ HeaderBar(app)
+ .tapAccountButton()
- AccountPage(app)
- .tapLogOutButton()
+ AccountPage(app)
+ .tapLogOutButton()
+ } else {
+ // Workaround for revoked device view not showing account button
+ RevokedDevicePage(app)
+ .tapGoToLogin()
+ }
LoginPage(app)
}
diff --git a/ios/MullvadVPNUITests/Test base classes/LoggedInWithTimeUITestCase.swift b/ios/MullvadVPNUITests/Test base classes/LoggedInWithTimeUITestCase.swift
index fc1fa747f8..fcf6d0f5e2 100644
--- a/ios/MullvadVPNUITests/Test base classes/LoggedInWithTimeUITestCase.swift
+++ b/ios/MullvadVPNUITests/Test base classes/LoggedInWithTimeUITestCase.swift
@@ -14,6 +14,7 @@ class LoggedInWithTimeUITestCase: BaseUITestCase {
super.setUp()
agreeToTermsOfServiceIfShown()
+ dismissChangeLogIfShown()
logoutIfLoggedIn()
login(accountNumber: hasTimeAccountNumber)
diff --git a/ios/MullvadVPNUITests/Test base classes/LoggedInWithoutTimeUITestCase.swift b/ios/MullvadVPNUITests/Test base classes/LoggedInWithoutTimeUITestCase.swift
index c7ab76e31b..8c40c4e76a 100644
--- a/ios/MullvadVPNUITests/Test base classes/LoggedInWithoutTimeUITestCase.swift
+++ b/ios/MullvadVPNUITests/Test base classes/LoggedInWithoutTimeUITestCase.swift
@@ -14,6 +14,7 @@ class LoggedInWithoutTimeUITestCase: BaseUITestCase {
super.setUp()
agreeToTermsOfServiceIfShown()
+ dismissChangeLogIfShown()
logoutIfLoggedIn()
login(accountNumber: noTimeAccountNumber)
diff --git a/ios/MullvadVPNUITests/Test base classes/LoggedOutUITestCase.swift b/ios/MullvadVPNUITests/Test base classes/LoggedOutUITestCase.swift
index a89c6e732e..64d0414296 100644
--- a/ios/MullvadVPNUITests/Test base classes/LoggedOutUITestCase.swift
+++ b/ios/MullvadVPNUITests/Test base classes/LoggedOutUITestCase.swift
@@ -14,6 +14,7 @@ class LoggedOutUITestCase: BaseUITestCase {
super.setUp()
agreeToTermsOfServiceIfShown()
+ dismissChangeLogIfShown()
logoutIfLoggedIn()
// Relaunch app so that tests start from a deterministic state
diff --git a/ios/TestPlans/MullvadVPNUITestsAll.xctestplan b/ios/TestPlans/MullvadVPNUITestsAll.xctestplan
index 29951c80fe..14cb0990dc 100644
--- a/ios/TestPlans/MullvadVPNUITestsAll.xctestplan
+++ b/ios/TestPlans/MullvadVPNUITestsAll.xctestplan
@@ -17,9 +17,11 @@
},
"testTargets" : [
{
- "selectedTests" : [
- "AccountTests\/testLogin()",
- "AccountTests\/testLoginWithIncorrectAccountNumber()"
+ "skippedTests" : [
+ "BaseUITestCase",
+ "LoggedInWithTimeUITestCase",
+ "LoggedInWithoutTimeUITestCase",
+ "LoggedOutUITestCase"
],
"target" : {
"containerPath" : "container:MullvadVPN.xcodeproj",
diff --git a/mullvad-api/src/ios_ffi.rs b/mullvad-api/src/ios_ffi.rs
deleted file mode 100644
index 7184723036..0000000000
--- a/mullvad-api/src/ios_ffi.rs
+++ /dev/null
@@ -1,374 +0,0 @@
-use std::{ffi::CString, net::SocketAddr, ptr, sync::Arc};
-
-use crate::{
- rest::{self, MullvadRestHandle},
- AccountsProxy, DevicesProxy,
-};
-
-#[derive(Debug, PartialEq)]
-#[repr(C)]
-pub enum MullvadApiErrorKind {
- NoError = 0,
- StringParsing = -1,
- SocketAddressParsing = -2,
- AsyncRuntimeInitialization = -3,
- BadResponse = -4,
- BufferTooSmall = -5,
-}
-
-/// MullvadApiErrorKind contains a description and an error kind. If the error kind is
-/// `MullvadApiErrorKind` is NoError, the pointer will be nil.
-#[repr(C)]
-pub struct MullvadApiError {
- description: *mut i8,
- kind: MullvadApiErrorKind,
-}
-
-impl MullvadApiError {
- fn new(kind: MullvadApiErrorKind, error: &dyn std::error::Error) -> Self {
- let description = CString::new(format!("{error:?}")).unwrap_or_default();
- Self {
- description: description.into_raw(),
- kind,
- }
- }
-
- fn api_err(error: &rest::Error) -> Self {
- Self::new(MullvadApiErrorKind::BadResponse, error)
- }
-
- fn with_str(kind: MullvadApiErrorKind, description: &str) -> Self {
- let description = CString::new(description).unwrap_or_default();
- Self {
- description: description.into_raw(),
- kind,
- }
- }
-
- fn ok() -> MullvadApiError {
- Self {
- description: CString::new("").unwrap().into_raw(),
- kind: MullvadApiErrorKind::NoError,
- }
- }
-
- fn drop(self) {
- let _ = unsafe { CString::from_raw(self.description) };
- }
-}
-
-/// IosMullvadApiClient is an FFI interface to our `mullvad-api`. It is a thread-safe to accessing
-/// our API.
-#[derive(Clone)]
-#[repr(C)]
-pub struct IosMullvadApiClient {
- ptr: *const IosApiClientContext,
-}
-
-impl IosMullvadApiClient {
- fn new(context: IosApiClientContext) -> Self {
- let sync_context = Arc::new(context);
- let ptr = Arc::into_raw(sync_context);
- Self { ptr }
- }
-
- unsafe fn from_raw(self) -> Arc<IosApiClientContext> {
- unsafe {
- Arc::increment_strong_count(self.ptr);
- }
-
- Arc::from_raw(self.ptr)
- }
-}
-
-struct IosApiClientContext {
- tokio_runtime: tokio::runtime::Runtime,
- api_runtime: crate::Runtime,
- api_hostname: String,
-}
-
-impl IosApiClientContext {
- fn rest_handle(self: Arc<Self>) -> MullvadRestHandle {
- self.tokio_runtime.block_on(
- self.api_runtime
- .static_mullvad_rest_handle(self.api_hostname.clone()),
- )
- }
-
- fn devices_proxy(self: Arc<Self>) -> DevicesProxy {
- crate::DevicesProxy::new(self.rest_handle())
- }
-
- fn accounts_proxy(self: Arc<Self>) -> AccountsProxy {
- crate::AccountsProxy::new(self.rest_handle())
- }
-
- fn tokio_handle(self: &Arc<Self>) -> tokio::runtime::Handle {
- self.tokio_runtime.handle().clone()
- }
-}
-
-/// Paramters:
-/// `api_address`: pointer to UTF-8 string containing a socket address representation
-/// ("143.32.4.32:9090"), the port is mandatory.
-///
-/// `api_address_len`: size of the API address string
-#[no_mangle]
-pub extern "C" fn mullvad_api_initialize_api_runtime(
- context_ptr: *mut IosMullvadApiClient,
- api_address_ptr: *const u8,
- api_address_len: usize,
- hostname: *const u8,
- hostname_len: usize,
-) -> MullvadApiError {
- let Some(addr_str) = (unsafe { string_from_raw_ptr(api_address_ptr, api_address_len) }) else {
- return MullvadApiError::with_str(
- MullvadApiErrorKind::StringParsing,
- "Failed to parse API socket address string",
- );
- };
- let Some(api_hostname) = (unsafe { string_from_raw_ptr(hostname, hostname_len) }) else {
- return MullvadApiError::with_str(
- MullvadApiErrorKind::StringParsing,
- "Failed to parse API host name",
- );
- };
-
- let Ok(api_address): Result<SocketAddr, _> = addr_str.parse() else {
- return MullvadApiError::with_str(
- MullvadApiErrorKind::SocketAddressParsing,
- "Failed to parse API socket address",
- );
- };
-
- let mut runtime_builder = tokio::runtime::Builder::new_multi_thread();
-
- runtime_builder.worker_threads(2).enable_all();
- let tokio_runtime = match runtime_builder.build() {
- Ok(runtime) => runtime,
- Err(err) => {
- return MullvadApiError::new(MullvadApiErrorKind::AsyncRuntimeInitialization, &err);
- }
- };
-
- // It is imperative that the REST runtime is created within an async context, otherwise
- // ApiAvailability panics.
- let api_runtime = tokio_runtime.block_on(async {
- crate::Runtime::with_static_addr(tokio_runtime.handle().clone(), api_address)
- });
-
- let ios_context = IosApiClientContext {
- tokio_runtime,
- api_runtime,
- api_hostname,
- };
-
- let context = IosMullvadApiClient::new(ios_context);
-
- unsafe {
- std::ptr::write(context_ptr, context);
- }
-
- MullvadApiError::ok()
-}
-
-#[no_mangle]
-pub extern "C" fn mullvad_api_remove_all_devices(
- context: IosMullvadApiClient,
- account_str_ptr: *const u8,
- account_str_len: usize,
-) -> MullvadApiError {
- let ctx = unsafe { context.from_raw() };
- let Some(account) = (unsafe { string_from_raw_ptr(account_str_ptr, account_str_len) }) else {
- return MullvadApiError::with_str(
- MullvadApiErrorKind::StringParsing,
- "Failed to parse account number",
- );
- };
-
- let runtime = ctx.tokio_handle();
- let device_proxy = ctx.devices_proxy();
- let result = runtime.block_on(async move {
- let devices = device_proxy.list(account.clone()).await?;
- for device in devices {
- device_proxy.remove(account.clone(), device.id).await?;
- }
- Result::<_, rest::Error>::Ok(())
- });
-
- match result {
- Ok(()) => MullvadApiError::ok(),
- Err(err) => MullvadApiError::api_err(&err),
- }
-}
-
-#[no_mangle]
-pub extern "C" fn mullvad_api_get_expiry(
- context: IosMullvadApiClient,
- account_str_ptr: *const u8,
- account_str_len: usize,
- expiry_unix_timestamp: *mut i64,
-) -> MullvadApiError {
- let Some(account) = (unsafe { string_from_raw_ptr(account_str_ptr, account_str_len) }) else {
- return MullvadApiError::with_str(
- MullvadApiErrorKind::StringParsing,
- "Failed to parse account number",
- );
- };
-
- let ctx = unsafe { context.from_raw() };
- let runtime = ctx.tokio_handle();
-
- let account_proxy = ctx.accounts_proxy();
- let result: Result<_, rest::Error> = runtime.block_on(async move {
- let expiry = account_proxy.get_data(account).await?.expiry;
- let expiry_timestamp = expiry.timestamp();
-
- Ok(expiry_timestamp)
- });
-
- match result {
- Ok(expiry) => {
- // SAFETY: It is assumed that expiry_timestamp is a valid pointer to a `libc::timespec`
- unsafe {
- std::ptr::write(expiry_unix_timestamp, expiry);
- }
- MullvadApiError::ok()
- }
- Err(err) => MullvadApiError::api_err(&err),
- }
-}
-
-/// Args:
-/// context: `IosApiContext`
-/// public_key: a pointer to a valid 32 byte array representing a WireGuard public key
-#[no_mangle]
-pub extern "C" fn mullvad_api_add_device(
- context: IosMullvadApiClient,
- account_str_ptr: *const u8,
- account_str_len: usize,
- public_key_ptr: *const u8,
-) -> MullvadApiError {
- let Some(account) = (unsafe { string_from_raw_ptr(account_str_ptr, account_str_len) }) else {
- return MullvadApiError::with_str(
- MullvadApiErrorKind::StringParsing,
- "Failed to parse account number",
- );
- };
-
- let public_key_bytes: [u8; 32] = unsafe { std::ptr::read(public_key_ptr as *const _) };
- let public_key = public_key_bytes.into();
-
- let ctx = unsafe { context.from_raw() };
- let runtime = ctx.tokio_handle();
-
- let devices_proxy = ctx.devices_proxy();
-
- let result: Result<_, rest::Error> = runtime.block_on(async move {
- let (new_device, _) = devices_proxy.create(account, public_key).await?;
- Ok(new_device)
- });
-
- match result {
- Ok(_result) => MullvadApiError::ok(),
- Err(err) => MullvadApiError::api_err(&err),
- }
-}
-
-/// Args:
-/// context: `IosApiContext`
-/// account_str_ptr: A pointer to a byte buffer large enough to contain a valid account number
-/// string.
-/// account_str_len: A pointer to an unsigned pointer-sized integer specifying the length of the
-/// input buffer. If the buffer is big enough and a new account is created, it will contain the
-/// amount of bytes that were written to the buffer.
-#[no_mangle]
-pub extern "C" fn mullvad_api_create_account(
- context: IosMullvadApiClient,
- account_str_ptr: *mut u8,
- account_str_len: *mut usize,
-) -> MullvadApiError {
- let ctx = unsafe { context.from_raw() };
- let runtime = ctx.tokio_handle();
- let buffer_len = unsafe { ptr::read(account_str_len) };
-
- let accounts_proxy = ctx.accounts_proxy();
-
- let result: Result<_, rest::Error> = runtime.block_on(async move {
- let new_account = accounts_proxy.create_account().await?;
- Ok(new_account)
- });
-
- match result {
- Ok(new_account) => {
- let new_account_bytes = new_account.into_bytes();
- if new_account_bytes.len() > buffer_len {
- return MullvadApiError::with_str(
- MullvadApiErrorKind::BufferTooSmall,
- "Buffer for account number is too small",
- );
- }
- unsafe {
- ptr::copy(
- new_account_bytes.as_ptr(),
- account_str_ptr,
- new_account_bytes.len(),
- );
- }
-
- MullvadApiError::ok()
- }
- Err(err) => MullvadApiError::api_err(&err),
- }
-}
-
-/// Args:
-/// context: `IosApiContext`
-/// public_key: a pointer to a valid 32 byte array representing a WireGuard public key
-#[no_mangle]
-pub extern "C" fn mullvad_api_delete_account(
- context: IosMullvadApiClient,
- account_str_ptr: *const u8,
- account_str_len: usize,
-) -> MullvadApiError {
- let ctx = unsafe { context.from_raw() };
- let runtime = ctx.tokio_handle();
-
- let Some(account) = (unsafe { string_from_raw_ptr(account_str_ptr, account_str_len) }) else {
- return MullvadApiError::with_str(
- MullvadApiErrorKind::StringParsing,
- "Failed to parse account number",
- );
- };
-
- let accounts_proxy = ctx.accounts_proxy();
-
- let result: Result<_, rest::Error> = runtime.block_on(async move {
- let new_account = accounts_proxy.delete_account(account).await?;
- Ok(new_account)
- });
-
- match result {
- Ok(()) => MullvadApiError::ok(),
- Err(err) => MullvadApiError::api_err(&err),
- }
-}
-
-#[no_mangle]
-pub extern "C" fn mullvad_api_runtime_drop(context: IosMullvadApiClient) {
- unsafe { Arc::decrement_strong_count(context.ptr) }
-}
-
-/// The return value is only valid for the lifetime of the `ptr` that's passed in
-///
-/// SAFETY: `ptr` must be valid for `size` bytes
-unsafe fn string_from_raw_ptr(ptr: *const u8, size: usize) -> Option<String> {
- let slice = unsafe { std::slice::from_raw_parts(ptr, size) };
-
- String::from_utf8(slice.to_vec()).ok()
-}
-
-#[no_mangle]
-pub extern "C" fn mullvad_api_error_drop(error: MullvadApiError) {
- error.drop()
-}
diff --git a/mullvad-api/src/lib.rs b/mullvad-api/src/lib.rs
index d7bc6acb47..c4d5665a01 100644
--- a/mullvad-api/src/lib.rs
+++ b/mullvad-api/src/lib.rs
@@ -454,27 +454,6 @@ impl Runtime {
rest::MullvadRestHandle::new(service, factory, self.availability_handle())
}
- /// This is only to be used in test code
- pub async fn static_mullvad_rest_handle(&self, hostname: String) -> rest::MullvadRestHandle {
- let service = self
- .new_request_service(
- Some(hostname.clone()),
- futures::stream::repeat(ApiConnectionMode::Direct),
- #[cfg(target_os = "android")]
- self.socket_bypass_tx.clone(),
- )
- .await;
- let token_store = access::AccessTokenStore::new(service.clone());
- let factory = rest::RequestFactory::new(hostname, Some(token_store));
-
- rest::MullvadRestHandle::new(
- service,
- factory,
- self.address_cache.clone(),
- self.availability_handle(),
- )
- }
-
/// Returns a new request service handle
pub fn rest_handle(&self) -> rest::RequestServiceHandle {
self.new_request_service(