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 Feb 26, 2024
1 parent 2859c5b commit 3d803f4
Show file tree
Hide file tree
Showing 29 changed files with 358 additions and 415 deletions.
85 changes: 74 additions & 11 deletions ios/MullvadVPN.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -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
}
7 changes: 7 additions & 0 deletions ios/MullvadVPN/Classes/AccessbilityIdentifier.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ public enum AccessibilityIdentifier: String {
case cancelButton
case connectionPanelButton
case collapseButton
case createAccountButton
case deleteButton
case disconnectButton
case infoButton
Expand Down Expand Up @@ -47,19 +48,24 @@ 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 welcomeView
case deleteAccountView

// Other UI elements
case connectionPanelInAddressRow
Expand All @@ -70,6 +76,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
40 changes: 40 additions & 0 deletions ios/MullvadVPNUITests/AccountTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,46 @@ class AccountTests: LoggedOutUITestCase {
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
59 changes: 58 additions & 1 deletion 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 @@ -11,18 +11,27 @@ import XCTest

enum MullvadAPIError: Error {
case incorrectConfigurationFormat
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

init() throws {
let apiAddress = try Self.getAPIIPAddress() + ":" + Self.getAPIPort()
let hostname = Self.getAPIHostname()
mullvadAPI = try MullvadApi(apiAddress: apiAddress, hostname: hostname)
}

public static func getAPIHostname() -> String {
return hostName
}
Expand All @@ -42,4 +51,52 @@ class MullvadAPIWrapper {

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
}
}
5 changes: 5 additions & 0 deletions ios/MullvadVPNUITests/Pages/LoginPage.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down
19 changes: 19 additions & 0 deletions ios/MullvadVPNUITests/Pages/OutOfTimePage.swift
Original file line number Diff line number Diff line change
@@ -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()
}
}
1 change: 1 addition & 0 deletions ios/MullvadVPNUITests/Pages/SelectLocationPage.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ class SelectLocationPage: Page {
super.init(app)

self.pageAccessibilityIdentifier = .selectLocationView
waitForPageToBeShown()
}

@discardableResult func tapLocationCell(withName name: String) -> Self {
Expand Down
Loading

0 comments on commit 3d803f4

Please sign in to comment.