mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-10-31 16:24:11 +00:00
fb49f5b83b
Add the element-call-ios dependency and a developer option for native calls The package carries the call UI and the matrix-rust-rtc media stack, pinned by exact version the way the SDK is, with the local path kept commented out for working against a checkout. Its media xcframework has no Intel simulator slice and a package cannot say so for the projects that consume it, hence the excluded architecture at the project level. The developer option is off by default and nothing reads it yet. It is placed next to the Element Call URL override, which only applies to the web view that native calls replace.
111 lines
4.1 KiB
Swift
111 lines
4.1 KiB
Swift
//
|
|
// Copyright 2026 Element Creations Ltd.
|
|
//
|
|
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
|
// Please see LICENSE files in the repository root for full details.
|
|
//
|
|
|
|
import Combine
|
|
import ElementCall
|
|
@testable import ElementX
|
|
import Testing
|
|
|
|
@MainActor
|
|
final class NativeCallStackTests {
|
|
private let stack: NativeCallStack
|
|
private var presentations: [NativeCallPresentation] = []
|
|
private var cancellables = Set<AnyCancellable>()
|
|
|
|
init() {
|
|
stack = NativeCallStack(transport: ElementCallFakeTransport(),
|
|
system: ElementCallFakeSystem(),
|
|
options: ElementCallOptions(),
|
|
style: .stock)
|
|
|
|
stack.actions
|
|
.sink { [weak self] presentation in
|
|
self?.presentations.append(presentation)
|
|
}
|
|
.store(in: &cancellables)
|
|
}
|
|
|
|
@Test
|
|
func startingACallAsksForItsScreen() {
|
|
stack.handleCallRequest(roomProxy: roomProxy(id: "!room:example.com"), isVoiceCall: false)
|
|
|
|
#expect(presentations == [.present])
|
|
#expect(stack.controller.room?.roomID == "!room:example.com")
|
|
}
|
|
|
|
@Test
|
|
func aCallStartsARoomWithoutOneAndJoinsARoomWithOne() {
|
|
stack.handleCallRequest(roomProxy: roomProxy(id: "!empty:example.com", hasOngoingCall: false), isVoiceCall: true)
|
|
// Starting rings the room; joining one already running happens quietly.
|
|
#expect(stack.controller.callData?.isStartingCall == true)
|
|
#expect(stack.controller.callData?.isAudioCall == true)
|
|
|
|
stack.stop()
|
|
|
|
let joining = NativeCallStack(transport: ElementCallFakeTransport(),
|
|
system: ElementCallFakeSystem(),
|
|
options: ElementCallOptions(),
|
|
style: .stock)
|
|
joining.handleCallRequest(roomProxy: roomProxy(id: "!busy:example.com", hasOngoingCall: true), isVoiceCall: true)
|
|
#expect(joining.controller.callData?.isStartingCall == false)
|
|
}
|
|
|
|
@Test
|
|
func askingForTheCallYouAreAlreadyInRestoresItsScreen() {
|
|
let roomProxy = roomProxy(id: "!room:example.com")
|
|
stack.handleCallRequest(roomProxy: roomProxy, isVoiceCall: false)
|
|
presentations.removeAll()
|
|
|
|
// Reached while the call is still joining, before the service has an ongoing call of its own.
|
|
stack.handleCallRequest(roomProxy: roomProxy, isVoiceCall: false)
|
|
|
|
#expect(presentations == [.restore])
|
|
}
|
|
|
|
@Test
|
|
func aCallInAnotherRoomWaitsForTheRunningOneToEnd() async throws {
|
|
stack.handleCallRequest(roomProxy: roomProxy(id: "!first:example.com"), isVoiceCall: false)
|
|
presentations.removeAll()
|
|
|
|
// The controller ignores a second call, so this one is queued behind a hang up.
|
|
stack.handleCallRequest(roomProxy: roomProxy(id: "!second:example.com"), isVoiceCall: false)
|
|
#expect(stack.controller.room?.roomID == "!first:example.com")
|
|
|
|
try await waitForPresentations([.dismiss, .present])
|
|
#expect(stack.controller.room?.roomID == "!second:example.com")
|
|
}
|
|
|
|
@Test
|
|
func endingACallDismissesItsScreen() async throws {
|
|
stack.handleCallRequest(roomProxy: roomProxy(id: "!room:example.com"), isVoiceCall: false)
|
|
presentations.removeAll()
|
|
|
|
stack.controller.hangUp()
|
|
|
|
try await waitForPresentations([.dismiss])
|
|
#expect(stack.controller.room == nil, "The controller is reset so the next call starts clean")
|
|
}
|
|
|
|
// MARK: - Helpers
|
|
|
|
private func roomProxy(id: String, hasOngoingCall: Bool = false) -> JoinedRoomProxyMock {
|
|
JoinedRoomProxyMock(.init(id: id, hasOngoingCall: hasOngoingCall))
|
|
}
|
|
|
|
private func waitForPresentations(_ expected: [NativeCallPresentation]) async throws {
|
|
let deferred = deferFulfillment(stack.actions) { [weak self] _ in
|
|
self?.presentations == expected
|
|
}
|
|
|
|
if presentations == expected {
|
|
return
|
|
}
|
|
|
|
try await deferred.fulfill()
|
|
}
|
|
}
|