Skip to content
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ package struct OnPremHeaderView: View {
+ Text(Image(systemName: "info.circle"))
.foregroundColor(.gray)
})
.accessibilityIdentifier("onPremInfoButton")
.multilineTextAlignment(.center)
.font(.textStyle(.h2))
.lineLimit(nil)
Expand Down
8 changes: 8 additions & 0 deletions wire-ios/Tests/TestPlans/UITests.xctestplan
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,14 @@
{
"key" : "INBUCKET_PASSWORD",
"value" : "$(INBUCKET_PASSWORD)"
},
{
"key" : "ANTA_DEEPLINK_URL",
"value" : "$(ANTA_DEEPLINK_URL)"
},
{
"key" : "ANTA_INBUCKET_URL",
"value" : "$(ANTA_INBUCKET_URL)"
}
],
"targetForVariableExpansion" : {
Expand Down
5 changes: 3 additions & 2 deletions wire-ios/Wire-iOS/Sources/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -351,7 +351,7 @@
) {
WireLogger.appDelegate.info("application:performFetchWithCompletionHandler:", attributes: .safePublic)

appRootRouter?.performWhenAuthenticated {

Check warning on line 354 in wire-ios/Wire-iOS/Sources/AppDelegate.swift

View workflow job for this annotation

GitHub Actions / Test Results

'shared()' is deprecated: This shared instance has been deprecated. Don't use it.

'shared()' is deprecated: This shared instance has been deprecated. Don't use it.
ZMUserSession.shared()?.application(application, performFetchWithCompletionHandler: completionHandler)
}
}
Expand All @@ -366,7 +366,7 @@
"application:handleEventsForBackgroundURLSession:completionHandler: session identifier: \(identifier)"
)

appRootRouter?.performWhenAuthenticated {

Check warning on line 369 in wire-ios/Wire-iOS/Sources/AppDelegate.swift

View workflow job for this annotation

GitHub Actions / Test Results

'shared()' is deprecated: This shared instance has been deprecated. Don't use it.

'shared()' is deprecated: This shared instance has been deprecated. Don't use it.
ZMUserSession.shared()?.application(
application,
handleEventsForBackgroundURLSession: identifier,
Expand Down Expand Up @@ -506,11 +506,12 @@
}

private func fetchDefaultEnvironment() -> BackendEnvironment2 {
let env = ProcessInfo.processInfo.arguments.contains("--useEnvStaging") ? "staging" : "default"
guard let path = Bundle.backendBundle.path(
forResource: "default",
forResource: env,
ofType: "json"
) else {
fatalError("default.json missing in Backend.bundle")
fatalError("\(env).json missing in Backend.bundle")
}

do {
Expand Down
3 changes: 2 additions & 1 deletion wire-ios/WireUITests/ApiClients/InbucketClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ enum InbucketClient {
let envVariables = try EnvironmentVariables()

var verificationCode = ""
let requestUrl = envVariables.inbucketURL.appending(path: "api/v1/mailbox/\(email)/latest")
let baseURL: URL = (BackendContext.current == .anta) ? envVariables.antaInbucketURL : envVariables.inbucketURL
let requestUrl = baseURL.appending(path: "api/v1/mailbox/\(email)/latest")

var request = URLRequest(url: requestUrl)
request.httpMethod = "GET"
Expand Down
25 changes: 25 additions & 0 deletions wire-ios/WireUITests/Helper/BackendContext.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
//
// Wire
// Copyright (C) 2025 Wire Swiss GmbH
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see http://www.gnu.org/licenses/.
//

import Foundation

public enum BackendContext {
public static var current: BackendTarget = .staging
}

public enum BackendTarget { case staging, anta }
15 changes: 15 additions & 0 deletions wire-ios/WireUITests/Helper/EnvironmentVariables.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,15 @@ struct EnvironmentVariables {
case missingInbucketURL
case missingInbucketUsername
case missingInbucketPassword
case missingDeepLinkURL
}

var backendURL: URL
var inbucketURL: URL
var inbucketUsername: String
var inbucketPassword: String
var antaDeepLinkURL: URL
var antaInbucketURL: URL

init() throws {
guard let backendURLString = ProcessInfo.processInfo.environment["BACKEND_URL"],
Expand All @@ -50,9 +53,21 @@ struct EnvironmentVariables {
throw Failure.missingInbucketUsername
}

guard let antaDeeplinkURL = ProcessInfo.processInfo.environment["ANTA_DEEPLINK_URL"],
!antaDeeplinkURL.isEmpty else {
throw Failure.missingDeepLinkURL
}

guard let antaInbucketURL = ProcessInfo.processInfo.environment["ANTA_INBUCKET_URL"],
!antaInbucketURL.isEmpty else {
throw Failure.missingInbucketURL
}

self.backendURL = URL(string: "https://\(backendURLString)")!
self.inbucketURL = URL(string: "https://\(inbucketHostname)")!
self.inbucketUsername = inbucketUsername
self.inbucketPassword = inbucketPassword
self.antaDeepLinkURL = URL(string: "https://\(antaDeeplinkURL)")!
self.antaInbucketURL = URL(string: "https://\(antaInbucketURL)")!
}
}
2 changes: 1 addition & 1 deletion wire-ios/WireUITests/Helper/UserHelper.swift
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ class UserHelper {
cookieStorage.cookies = cookies

// Get activation code
let (activationCode, activationKey) = try await BackendClient.getActivationCode(email: user.email)
let (activationCode, activationKey) = try await authenticationAPI.getActivationCode(forEmail: user.email)

// Activate user
try await authenticationAPI.activateUser(email: user.email, key: activationKey, code: activationCode)
Expand Down
37 changes: 34 additions & 3 deletions wire-ios/WireUITests/Helper/WireUITestCase.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,7 @@ class WireUITestCase: XCTestCase {

let launchArguments = [
"-resetData",
"--BackendEnvironmentTypeOverrideKey=staging",
"--persist-backend-type",
"--useEnvStaging",
"--preferred-api-version=8"
]

Expand All @@ -42,7 +41,7 @@ class WireUITestCase: XCTestCase {
app.launchArguments = launchArguments
app.setDeveloperFlags([
.useWireAuthentication: true,
.multibackend: false
.multibackend: true
])
app.launch()

Expand All @@ -55,4 +54,36 @@ class WireUITestCase: XCTestCase {
await userHelper.deleteCreatedUsers()
}

func setCustomBackend(byDeeplink deeplink: URL, timeout: TimeInterval = 5, domainInfo: String) {
XCTContext.runActivity(named: "Set custom backend via deeplink") { _ in
let deeplinkFullURL = "wire://access/?config=\(deeplink)"
guard let url = URL(string: deeplinkFullURL) else {
XCTFail("Invalid deeplink: \(deeplinkFullURL)")
return
}

XCUIDevice.shared.system.open(url)

let alert = springboard.alerts.firstMatch
if alert.waitForExistence(timeout: 2) {
let openButton = springboard.alerts.buttons
.matching(NSPredicate(format: "label BEGINSWITH[c] 'Open'"))
.firstMatch
if openButton.waitForExistence(timeout: 1) {
openButton.tap()
}
}

XCTAssertTrue(
app.wait(for: .runningForeground, timeout: timeout),
"App did not return to foreground after opening deeplink"
)
app.buttons["Proceed"].tap()
let welcomeLabel = app.descendants(matching: .any)["onPremInfoButton"].label
XCTAssertTrue(
welcomeLabel.contains(domainInfo),
"Expected domain missing from \(welcomeLabel)"
)
}
}
}
112 changes: 112 additions & 0 deletions wire-ios/WireUITests/MultiBackendSupportTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
//
// Wire
// Copyright (C) 2025 Wire Swiss GmbH
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see http://www.gnu.org/licenses/.
//

import XCTest

final class MultiBackendSupportTests: WireUITestCase {

@MainActor
func test_Add_MultiBackend_Accounts() async throws {

defer { BackendContext.current = .staging }
let domainInfoStaging = "staging.zinfra.io"
let domainInfoAnta = "anta.wire.link"

let user_Backend1 = try await userHelper.createPersonalUser()

let firstTimePage = try app.loginUser(email: user_Backend1.email, password: user_Backend1.password)
var accountPage = try firstTimePage.acceptPopup()
.openSettings()
.openAccountSettings()

// Verify backend1-staging details
var accountName = try XCTUnwrap(accountPage.getAccountName())
var domanInfo = try XCTUnwrap(accountPage.getDomainInfo())
XCTAssertEqual(accountName, user_Backend1.name, "Account name didn't match \(user_Backend1.name)")
XCTAssertTrue(
accountPage.getUsername().contains(user_Backend1.username),
"Username didn't contain \(user_Backend1.username)"
)
XCTAssertEqual(accountPage.getEmail(), user_Backend1.email, "Email didn't contain \(user_Backend1.email)")
XCTAssertEqual(domanInfo, domainInfoStaging, "Domain info \(domanInfo) mismatched on account page")

_ = try accountPage.backToSettings()
.switchToConversationsTab()
.openUserAccountPageForUser(with: user_Backend1.name)
.tapAddAccountOrTeamButton()

let deeplink = try EnvironmentVariables().antaDeepLinkURL
setCustomBackend(byDeeplink: deeplink, domainInfo: domainInfoAnta)
BackendContext.current = .anta

// Register for backend2 - anta
let user_Backend2 = UserGenerator.generateUniqueUserInfo()

let welcomePage = try WelcomePage()

let createPersonalAccountFormPage = try welcomePage
.enterEmailOrSSO(user_Backend2.email)
.tapCreatePersonalAccountLink()

let verificationPage = try createPersonalAccountFormPage
.enterName(user_Backend2.name)
.enterPassword(user_Backend2.password)
.enterConfirmPassword(user_Backend2.password)
.tapContinueButton()
.tapAcceptButton()

let verificationCode = try await InbucketClient.getVerificationCode(email: user_Backend2.email)

let setUsernamePage = try verificationPage
.enterVerificationCodeAndConfirm(verificationCode)

accountPage = try setUsernamePage
.setUsername(user_Backend2.username)
.openSettings()
.openAccountSettings()

// Verify backend2-anta details
accountName = try XCTUnwrap(accountPage.getAccountName())
domanInfo = try XCTUnwrap(accountPage.getDomainInfo())
XCTAssertEqual(accountName, user_Backend2.name, "Account name didn't match \(user_Backend2.name)")
XCTAssertTrue(
accountPage.getUsername().contains(user_Backend2.username),
"Username didn't contain \(user_Backend2.username)"
)
XCTAssertEqual(accountPage.getEmail(), user_Backend2.email, "Email didn't contain \(user_Backend2.email)")
XCTAssertEqual(domanInfo, domainInfoAnta, "Domain info \(domanInfo) mismatched on account page")

accountPage = try accountPage.backToSettings()
.switchToConversationsTab()
.openUserAccountPageForUser(with: user_Backend2.name)
.switchUserAccountForUser(withName: user_Backend1.name)
.openSettings()
.openAccountSettings()

// Verify backend1-staging details - after switching account
accountName = try XCTUnwrap(accountPage.getAccountName())
domanInfo = try XCTUnwrap(accountPage.getDomainInfo())
XCTAssertEqual(accountName, user_Backend1.name, "Account name didn't match \(user_Backend1.name)")
XCTAssertTrue(
accountPage.getUsername().contains(user_Backend1.username),
"Username didn't contain \(user_Backend1.username)"
)
XCTAssertEqual(accountPage.getEmail(), user_Backend1.email, "Email didn't contain \(user_Backend1.email)")
XCTAssertEqual(domanInfo, domainInfoStaging, "Domain info \(domanInfo) mismatched on account page")
}
}
17 changes: 15 additions & 2 deletions wire-ios/WireUITests/Pages/AccountSettingsPage.swift
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ class AccountSettingsPage: PageModel {
app.descendants(matching: .any)["EmailField"].firstMatch
}

var domainField: XCUIElement {
app.descendants(matching: .any)["DomainFieldDisabled"].firstMatch
}

var logoutButton: XCUIElement {
app.staticTexts["Log Out"]
}
Expand All @@ -48,6 +52,10 @@ class AccountSettingsPage: PageModel {
app.buttons["OK"]
}

var backToSettingsButton: XCUIElement {
app.buttons["Settings"]
}

var backupOrRestoreButton: XCUIElement {
app.descendants(matching: .any)["Back up or RestoreField"].firstMatch
}
Expand All @@ -64,8 +72,13 @@ class AccountSettingsPage: PageModel {
emailField.label
}

var backToSettingsButton: XCUIElement {
app.buttons["Settings"]
func getDomainInfo() -> String {
domainField.value as! String
}

func backToSettings() throws -> SettingsPage {
backToSettingsButton.tap()
return try SettingsPage()
}

@discardableResult
Expand Down
6 changes: 3 additions & 3 deletions wire-ios/WireUITests/Pages/UserAccountPage.swift
Original file line number Diff line number Diff line change
Expand Up @@ -42,15 +42,15 @@ class UserAccountPage: PageModel {
}

var manageTeamButton: XCUIElement {
app.staticTexts["Manage Team"].firstMatch
app.staticTexts["Manage Team & Billing"].firstMatch
}

var closeButton: XCUIElement {
app.descendants(matching: .any)["close"].firstMatch
}

var addAccountOrTeamButton: XCUIElement {
app.descendants(matching: .any)["Add Account or TeamField"]
app.descendants(matching: .any)["Add Account or Team"].firstMatch
}

func tapCreateTeamButtonAndContinue() throws -> TeamSetupStepsPage {
Expand All @@ -73,7 +73,7 @@ class UserAccountPage: PageModel {
}

func switchUserAccountForUser(withName name: String) throws -> ConversationsPage {
let predicate = NSPredicate(format: "value BEGINSWITH %@", name)
let predicate = NSPredicate(format: "label BEGINSWITH %@", name)
let button = app.buttons.containing(predicate).firstMatch
button.tap()
return try ConversationsPage()
Expand Down
3 changes: 0 additions & 3 deletions wire-ios/WireUITests/PersonalUserTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -135,8 +135,5 @@ final class PersonalUsersTests: WireUITestCase {
let accountNameUserB = try XCTUnwrap(accountSettingsPage.getAccountName())

XCTAssertNotEqual(accountNameUserA, accountNameUserB, "Account name didn't change after deleting")

try accountSettingsPage.logout()
.enterPassword(userB.password)
}
}
2 changes: 2 additions & 0 deletions wire-ios/WireUITests/env.xcconfig.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,5 @@ INBUCKET_PASSWORD=op://Test Automation/BackendConnection staging/inbucketPasswor
INBUCKET_URL=op://Test Automation/BackendConnection staging/trimmedInbucketUrl
BACKEND_URL=op://Test Automation/BackendConnection staging/trimmedBackendURL
BASIC_AUTH=op://Test Automation/BackendConnection staging/basicAuth
ANTA_DEEPLINK_URL=op://Test Automation/BackendConnection anta/trimmedDeeplinkUrl
ANTA_INBUCKET_URL=op://Test Automation/BackendConnection anta/trimmedInbucketUrl
Loading