Skip to content
Merged
Show file tree
Hide file tree
Changes from 17 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
Original file line number Diff line number Diff line change
Expand Up @@ -68,10 +68,12 @@ public protocol AuthenticationAPI: Sendable {
func requestVerificationCode(for email: String) async throws

/// Get Activation key & code for email
/// - Parameter email: email of user
/// - Parameters:
/// - email: email of user
/// - basicAuth: basicAuth value
/// - Returns: Code & Key
#if DEBUG
func getActivationCode(forEmail email: String) async throws -> (code: String, key: String)
func getActivationCode(forEmail email: String, basicAuth: String) async throws -> (code: String, key: String)
#endif

/// Register Personal Account
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@

import Foundation
import WireFoundation

Check warning on line 21 in WireNetwork/Sources/WireNetwork/APIs/Rest/AuthenticationAPI/AuthenticationAPIV0.swift

View workflow job for this annotation

GitHub Actions / Test Results

Non-final class 'AuthenticationAPIV0' cannot conform to 'Sendable'; use '@unchecked Sendable'; this is an error in the Swift 6 language mode

Non-final class 'AuthenticationAPIV0' cannot conform to 'Sendable'; use '@unchecked Sendable'; this is an error in the Swift 6 language mode

Check warning on line 21 in WireNetwork/Sources/WireNetwork/APIs/Rest/AuthenticationAPI/AuthenticationAPIV0.swift

View workflow job for this annotation

GitHub Actions / Test Results

Non-final class 'AuthenticationAPIV0' cannot conform to 'Sendable'; use '@unchecked Sendable'; this is an error in the Swift 6 language mode

Non-final class 'AuthenticationAPIV0' cannot conform to 'Sendable'; use '@unchecked Sendable'; this is an error in the Swift 6 language mode
class AuthenticationAPIV0: AuthenticationAPI, VersionedAPI {

Check warning on line 23 in WireNetwork/Sources/WireNetwork/APIs/Rest/AuthenticationAPI/AuthenticationAPIV0.swift

View workflow job for this annotation

GitHub Actions / Test Results

Stored property 'networkService' of 'Sendable'-conforming class 'AuthenticationAPIV0' has non-sendable type 'any NetworkServiceProtocol'; this is an error in the Swift 6 language mode

Stored property 'networkService' of 'Sendable'-conforming class 'AuthenticationAPIV0' has non-sendable type 'any NetworkServiceProtocol'; this is an error in the Swift 6 language mode

Check warning on line 23 in WireNetwork/Sources/WireNetwork/APIs/Rest/AuthenticationAPI/AuthenticationAPIV0.swift

View workflow job for this annotation

GitHub Actions / Test Results

Stored property 'networkService' of 'Sendable'-conforming class 'AuthenticationAPIV0' has non-sendable type 'any NetworkServiceProtocol'; this is an error in the Swift 6 language mode

Stored property 'networkService' of 'Sendable'-conforming class 'AuthenticationAPIV0' has non-sendable type 'any NetworkServiceProtocol'; this is an error in the Swift 6 language mode
let networkService: any NetworkServiceProtocol

init(networkService: any NetworkServiceProtocol) {
Expand Down Expand Up @@ -192,18 +192,18 @@
.parse(code: response.statusCode, data: data)
}

func getActivationCode(forEmail email: String) async throws -> (code: String, key: String) {
func getActivationCode(forEmail email: String, basicAuth: String) async throws -> (code: String, key: String) {
let path = "/i/users/activation-code?email=\(email)"
let auth = ProcessInfo.processInfo.environment["BASIC_AUTH"]!

let request = try URLRequestBuilder(path: path)
.withMethod(.get)
.addingHeader(field: "Authorization", value: "Basic \(auth)")
.addingHeader(field: "Authorization", value: "Basic \(basicAuth)")
.build()

let (data, response) = try await networkService.executeRequest(request)

guard

Check warning on line 205 in WireNetwork/Sources/WireNetwork/APIs/Rest/AuthenticationAPI/AuthenticationAPIV0.swift

View workflow job for this annotation

GitHub Actions / Test Results

Immutable value 'responseURL' was never used; consider replacing with '_' or removing it

Immutable value 'responseURL' was never used; consider replacing with '_' or removing it

Check warning on line 205 in WireNetwork/Sources/WireNetwork/APIs/Rest/AuthenticationAPI/AuthenticationAPIV0.swift

View workflow job for this annotation

GitHub Actions / Test Results

Immutable value 'responseURL' was never used; consider replacing with '_' or removing it

Immutable value 'responseURL' was never used; consider replacing with '_' or removing it
let responseURL = response.url,

Check warning on line 206 in WireNetwork/Sources/WireNetwork/APIs/Rest/AuthenticationAPI/AuthenticationAPIV0.swift

View workflow job for this annotation

GitHub Actions / Test Results

Immutable value 'responseHeaders' was never used; consider replacing with '_' or removing it

Immutable value 'responseHeaders' was never used; consider replacing with '_' or removing it

Check warning on line 206 in WireNetwork/Sources/WireNetwork/APIs/Rest/AuthenticationAPI/AuthenticationAPIV0.swift

View workflow job for this annotation

GitHub Actions / Test Results

Immutable value 'responseHeaders' was never used; consider replacing with '_' or removing it

Immutable value 'responseHeaders' was never used; consider replacing with '_' or removing it
let responseHeaders = response.allHeaderFields as? [String: String]
else {
throw AuthenticationAPIError.invalidResponse
Expand All @@ -228,7 +228,7 @@
.withMethod(.post)
.withBody(body, contentType: .json)
.build()

Check warning on line 231 in WireNetwork/Sources/WireNetwork/APIs/Rest/AuthenticationAPI/AuthenticationAPIV0.swift

View workflow job for this annotation

GitHub Actions / Test Results

Immutable value 'data' was never used; consider replacing with '_' or removing it

Immutable value 'data' was never used; consider replacing with '_' or removing it

Check warning on line 231 in WireNetwork/Sources/WireNetwork/APIs/Rest/AuthenticationAPI/AuthenticationAPIV0.swift

View workflow job for this annotation

GitHub Actions / Test Results

Immutable value 'data' was never used; consider replacing with '_' or removing it

Immutable value 'data' was never used; consider replacing with '_' or removing it
let (data, response) = try await networkService.executeRequest(request)

guard
Expand Down Expand Up @@ -260,11 +260,11 @@
.withMethod(.post)
.withBody(body, contentType: .json)
.build()

Check warning on line 263 in WireNetwork/Sources/WireNetwork/APIs/Rest/AuthenticationAPI/AuthenticationAPIV0.swift

View workflow job for this annotation

GitHub Actions / Test Results

Immutable value 'data' was never used; consider replacing with '_' or removing it

Immutable value 'data' was never used; consider replacing with '_' or removing it

Check warning on line 263 in WireNetwork/Sources/WireNetwork/APIs/Rest/AuthenticationAPI/AuthenticationAPIV0.swift

View workflow job for this annotation

GitHub Actions / Test Results

Immutable value 'response' was never used; consider replacing with '_' or removing it

Immutable value 'response' was never used; consider replacing with '_' or removing it

Check warning on line 263 in WireNetwork/Sources/WireNetwork/APIs/Rest/AuthenticationAPI/AuthenticationAPIV0.swift

View workflow job for this annotation

GitHub Actions / Test Results

Immutable value 'data' was never used; consider replacing with '_' or removing it

Immutable value 'data' was never used; consider replacing with '_' or removing it

Check warning on line 263 in WireNetwork/Sources/WireNetwork/APIs/Rest/AuthenticationAPI/AuthenticationAPIV0.swift

View workflow job for this annotation

GitHub Actions / Test Results

Immutable value 'response' was never used; consider replacing with '_' or removing it

Immutable value 'response' was never used; consider replacing with '_' or removing it
let (data, response) = try await networkService.executeRequest(request)

Check warning on line 265 in WireNetwork/Sources/WireNetwork/APIs/Rest/AuthenticationAPI/AuthenticationAPIV0.swift

View workflow job for this annotation

GitHub Actions / Test Results

No calls to throwing functions occur within 'try' expression

No calls to throwing functions occur within 'try' expression

Check warning on line 265 in WireNetwork/Sources/WireNetwork/APIs/Rest/AuthenticationAPI/AuthenticationAPIV0.swift

View workflow job for this annotation

GitHub Actions / Test Results

No calls to throwing functions occur within 'try' expression

No calls to throwing functions occur within 'try' expression
try ResponseParser()
.success(code: .ok)

Check warning on line 267 in WireNetwork/Sources/WireNetwork/APIs/Rest/AuthenticationAPI/AuthenticationAPIV0.swift

View workflow job for this annotation

GitHub Actions / Test Results

Result of call to 'failure(code:label:error:)' is unused

Result of call to 'failure(code:label:error:)' is unused

Check warning on line 267 in WireNetwork/Sources/WireNetwork/APIs/Rest/AuthenticationAPI/AuthenticationAPIV0.swift

View workflow job for this annotation

GitHub Actions / Test Results

Result of call to 'failure(code:label:error:)' is unused

Result of call to 'failure(code:label:error:)' is unused
.failure(code: .badRequest, label: "bad-request", error: AuthenticationAPIError.invalidEmail)
}

Expand Down
16 changes: 16 additions & 0 deletions wire-ios/Tests/TestPlans/UITests.xctestplan
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,22 @@
{
"key" : "INBUCKET_PASSWORD",
"value" : "$(INBUCKET_PASSWORD)"
},
{
"key" : "ANTA_DEEPLINK_URL",
"value" : "$(ANTA_DEEPLINK_URL)"
},
{
"key" : "ANTA_INBUCKET_URL",
"value" : "$(ANTA_INBUCKET_URL)"
},
{
"key" : "BACKEND_URL_ANTA",
"value" : "$(BACKEND_URL_ANTA)"
},
{
"key" : "BASIC_AUTH_ANTA",
"value" : "$(BASIC_AUTH_ANTA)"
}
],
"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 @@ -355,7 +355,7 @@
return
}

appRootRouter.performWhenAuthenticated {

Check warning on line 358 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 @@ -376,7 +376,7 @@
return
}

appRootRouter.performWhenAuthenticated {

Check warning on line 379 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 @@ -516,11 +516,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
4 changes: 3 additions & 1 deletion wire-ios/WireUITests/ApiClients/InbucketClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,16 @@
//

import Foundation
import WireNetwork

enum InbucketClient {

static func getVerificationCode(email: String) async throws -> String {
let envVariables = try EnvironmentVariables()

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

var request = URLRequest(url: requestUrl)
request.httpMethod = "GET"
Expand Down
47 changes: 47 additions & 0 deletions wire-ios/WireUITests/Helper/BackendContext.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
//
// 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
import WireNetwork

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

static var backendEnvironment: BackendEnvironment {
switch current {
case .staging:
.staging
case .anta:
.anta
}
}
}

public enum BackendTarget {
case staging
case anta

var domainInfo: String {
switch self {
case .staging:
"staging.zinfra.io"
case .anta:
"anta.wire.link"
}
}
}
65 changes: 59 additions & 6 deletions wire-ios/WireUITests/Helper/EnvironmentVariables.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,19 @@ struct EnvironmentVariables {
case missingInbucketURL
case missingInbucketUsername
case missingInbucketPassword
case missingDeepLinkURL
}

var backendURL: URL
var inbucketURL: URL
var inbucketUsername: String
var inbucketPassword: String
private let stagingBackendURL: URL
private let antaBackendURL: URL

private let stagingInbucketURL: URL
private let antaInbucketURL: URL

let antaDeepLinkURL: URL

let inbucketUsername: String
let inbucketPassword: String

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

self.backendURL = URL(string: "https://\(backendURLString)")!
self.inbucketURL = URL(string: "https://\(inbucketHostname)")!
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
}

guard let backendURLAntaString = ProcessInfo.processInfo.environment["BACKEND_URL_ANTA"],
!backendURLAntaString.isEmpty else {
throw Failure.missingBackendURL
}

self.stagingBackendURL = URL(string: "https://\(backendURLString)")!
self.stagingInbucketURL = URL(string: "https://\(inbucketHostname)")!
self.inbucketUsername = inbucketUsername
self.inbucketPassword = inbucketPassword
self.antaDeepLinkURL = URL(string: "https://\(antaDeeplinkURL)")!
self.antaInbucketURL = URL(string: "https://\(antaInbucketURL)")!
self.antaBackendURL = URL(string: "https://\(backendURLAntaString)")!
}

var inbucketURL: URL {
switch BackendContext.current {
case .anta:
antaInbucketURL
case .staging:
stagingInbucketURL
}
}

var backendURL: URL {
switch BackendContext.current {
case .anta:
antaBackendURL
case .staging:
stagingBackendURL
}
}

func deepLinkURL(for target: BackendTarget) -> URL {
switch target {
case .anta:
antaDeepLinkURL
case .staging:
fatalError("Not implemented yet")
}
}

}
40 changes: 36 additions & 4 deletions wire-ios/WireUITests/Helper/UserHelper.swift
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,10 @@ class UserHelper {
private let authenticationManager = MockAuthManager()

init(apiVersion: APIVersion = .v8) {

self.createdUsers = []
self.networkStack = NetworkStack(
backendEnvironment: .staging,
backendEnvironment: BackendContext.backendEnvironment,
minTLSVersion: .v1_2,
cookieEncryptionKey: Data(),
authenticationManager: authenticationManager
Expand All @@ -55,6 +56,22 @@ class UserHelper {
self.conversationsAPI = ConversationsAPIBuilder(apiService: networkStack.apiService).makeAPI(for: apiVersion)
}

func basicAuth(_ backend: BackendTarget = BackendContext.current) -> String {
switch backend {
case .staging:
guard let auth = ProcessInfo.processInfo.environment["BASIC_AUTH"] else {
fatalError("Missing BASIC_AUTH environment variable")
}
return auth

case .anta:
guard let auth = ProcessInfo.processInfo.environment["BASIC_AUTH_ANTA"] else {
fatalError("Missing BASIC_AUTH_ANTA environment variable")
}
return auth
}
}

func createPersonalUser() async throws -> UserInfo {
let user = UserGenerator.generateUniqueUserInfo()

Expand All @@ -67,7 +84,10 @@ 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,
basicAuth: basicAuth()
)

// Activate user
try await authenticationAPI.activateUser(email: user.email, key: activationKey, code: activationCode)
Expand Down Expand Up @@ -153,7 +173,10 @@ class UserHelper {
}

func fetchAccessToken(email: String, password: String) async throws -> String {
let (activationCode, activationKey) = try await authenticationAPI.getActivationCode(forEmail: email)
let (activationCode, activationKey) = try await authenticationAPI.getActivationCode(
forEmail: email,
basicAuth: basicAuth()
)

try await authenticationAPI.activateUser(email: email, key: activationKey, code: activationCode)

Expand Down Expand Up @@ -258,15 +281,24 @@ class UserHelper {
}
}

private extension BackendEnvironment {
extension BackendEnvironment {
static let backendURL = "https://\(ProcessInfo.processInfo.environment["BACKEND_URL"]!)"
static let backendURLAnta = "https://\(ProcessInfo.processInfo.environment["BACKEND_URL_ANTA"]!)"
static let staging = BackendEnvironment(
url: URL(string: backendURL)!,
webSocketURL: URL(string: backendURL)!,
blacklistURL: URL(string: backendURL)!,
pinnedKeys: [],
proxySettings: nil
)

static let anta = BackendEnvironment(
url: URL(string: backendURLAnta)!,
webSocketURL: URL(string: backendURLAnta)!,
blacklistURL: URL(string: backendURLAnta)!,
pinnedKeys: [],
proxySettings: nil
)
}

enum FilterConversationsByCriteria {
Expand Down
48 changes: 45 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,47 @@ 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"
)
guard let welcomePage = try? SetCustomBackendPage().tapOnProceedButton() else {
XCTFail("Failed to proceed to set custom backend")
return
}
let labeltext = welcomePage.setBackendLabel.label
XCTAssertTrue(
labeltext.contains(domainInfo),
"Expected domain missing from \(labeltext)"
)
}
}

func switchBackend(target: BackendTarget) throws {

let deeplink = try EnvironmentVariables().deepLinkURL(for: target)
setCustomBackend(byDeeplink: deeplink, domainInfo: target.domainInfo)
// need to change for Inbucket
BackendContext.current = target
}
}
Loading
Loading