Skip to content

Commit

Permalink
Add account creation and deletion tests
Browse files Browse the repository at this point in the history
  • Loading branch information
niklasberglund committed Mar 8, 2024
1 parent 81009ad commit 2fcd329
Show file tree
Hide file tree
Showing 32 changed files with 364 additions and 432 deletions.
61 changes: 47 additions & 14 deletions ios/MullvadVPN.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

9 changes: 9 additions & 0 deletions ios/MullvadVPN/Classes/AccessbilityIdentifier.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -47,20 +49,26 @@ 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
case settingsTableView
case tunnelControlView
case problemReportView
case problemReportSubmittedView
case revokedDeviceView
case welcomeView
case deleteAccountView

// Other UI elements
case connectionPanelInAddressRow
Expand All @@ -71,6 +79,7 @@ public enum AccessibilityIdentifier: String {
case selectLocationSearchTextField
case problemReportEmailTextField
case problemReportMessageTextView
case deleteAccountTextField

// DNS settings
case dnsSettings
Expand Down
1 change: 1 addition & 0 deletions ios/MullvadVPN/Coordinators/ChangeLogCoordinator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -346,6 +347,7 @@ class AccountDeletionContentView: UIView {
}

private func setupAppearance() {
accessibilityIdentifier = .deleteAccountView
translatesAutoresizingMaskIntoConstraints = false
backgroundColor = .secondaryColor
directionalLayoutMargins = UIMetrics.contentLayoutMargins
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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?
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,6 @@ class AlertViewController: UIViewController {
view.backgroundColor = .secondaryColor
view.layer.cornerRadius = 11

view.accessibilityIdentifier = .alertContainerView

return view
}()

Expand Down Expand Up @@ -112,6 +110,9 @@ class AlertViewController: UIViewController {

view.backgroundColor = .black.withAlphaComponent(0.5)

let accessibilityIdentifier = presentation.accessibilityIdentifier ?? .alertContainerView
view.accessibilityIdentifier = accessibilityIdentifier

setContent()
setConstraints()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ class OutOfTimeContentView: UIView {

override init(frame: CGRect) {
super.init(frame: frame)
accessibilityIdentifier = .outOfTimeView
translatesAutoresizingMaskIntoConstraints = false
backgroundColor = .secondaryColor
directionalLayoutMargins = UIMetrics.contentLayoutMargins
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down Expand Up @@ -108,6 +109,7 @@ class RevokedDeviceViewController: UIViewController, RootContainment {
override func viewDidLoad() {
super.viewDidLoad()

view.accessibilityIdentifier = .revokedDeviceView
view.backgroundColor = .secondaryColor
view.directionalLayoutMargins = UIMetrics.contentLayoutMargins

Expand Down
46 changes: 46 additions & 0 deletions ios/MullvadVPNUITests/AccountTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
4 changes: 4 additions & 0 deletions ios/MullvadVPNUITests/MullvadApi.swift
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,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,
Expand Down
65 changes: 59 additions & 6 deletions ios/MullvadVPNUITests/Networking/MullvadAPIWrapper.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//
// AppAPI.swift
// MullvadAPIWrapper.swift
// MullvadVPNUITests
//
// Created by Niklas Berglund on 2024-01-18.
Expand All @@ -10,36 +10,89 @@ import Foundation
import XCTest

enum MullvadAPIError: Error {
case incorrectConfigurationFormat
case invalidEndpointFormatError
case requestError
}

class MullvadAPIWrapper {
// swiftlint:disable force_cast
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
}

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
}
}
}
39 changes: 39 additions & 0 deletions ios/MullvadVPNUITests/Pages/AccountDeletionPage.swift
Original file line number Diff line number Diff line change
@@ -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
}
}
24 changes: 24 additions & 0 deletions ios/MullvadVPNUITests/Pages/ChangeLogAlert.swift
Original file line number Diff line number Diff line change
@@ -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
}
}
Loading

0 comments on commit 2fcd329

Please sign in to comment.