diff --git a/GliaWidgets/Component/Bubble/BubbleView.swift b/GliaWidgets/Component/Bubble/BubbleView.swift index 5dfc44305..69645d7ea 100644 --- a/GliaWidgets/Component/Bubble/BubbleView.swift +++ b/GliaWidgets/Component/Bubble/BubbleView.swift @@ -107,6 +107,7 @@ class BubbleView: UIView { accessibilityTraits = [.button] accessibilityLabel = style.accessibility.label accessibilityHint = style.accessibility.hint + accessibilityIdentifier = "bubble_view" update(kind) } diff --git a/GliaWidgets/Component/Connect/Status/ConnectStatusView.swift b/GliaWidgets/Component/Connect/Status/ConnectStatusView.swift index 42fed6aee..7504fd423 100644 --- a/GliaWidgets/Component/Connect/Status/ConnectStatusView.swift +++ b/GliaWidgets/Component/Connect/Status/ConnectStatusView.swift @@ -27,6 +27,7 @@ class ConnectStatusView: UIView { firstLabel.accessibilityHint = style.accessibility.firstTextHint secondLabel.accessibilityHint = style.accessibility.secondTextHint + secondLabel.accessibilityIdentifier = "call_duration_label" setFontScalingEnabled( style.accessibility.isFontScalingEnabled, for: firstLabel diff --git a/GliaWidgets/Component/Header/Header.swift b/GliaWidgets/Component/Header/Header.swift index f33a3857d..bb0b889a7 100644 --- a/GliaWidgets/Component/Header/Header.swift +++ b/GliaWidgets/Component/Header/Header.swift @@ -97,9 +97,10 @@ class Header: UIView { rightItemContainer.spacing = 8 rightItemContainer.alignment = .center - closeButton.accessibilityIdentifier = "chat_close_button" + backButton.accessibilityIdentifier = "header_back_button" + closeButton.accessibilityIdentifier = "header_close_button" closeButton.accessibilityLabel = style.closeButton.accessibility.label - endButton.accessibilityIdentifier = "chat_end_button" + endButton.accessibilityIdentifier = "header_end_button" endButton.accessibilityLabel = style.endButton.accessibility.label setFontScalingEnabled( style.accessibility.isFontScalingEnabled, diff --git a/GliaWidgets/CoreSDKClient/CoreSDKClient.Interface.swift b/GliaWidgets/CoreSDKClient/CoreSDKClient.Interface.swift index c51ace9f3..914fbecc1 100644 --- a/GliaWidgets/CoreSDKClient/CoreSDKClient.Interface.swift +++ b/GliaWidgets/CoreSDKClient/CoreSDKClient.Interface.swift @@ -146,6 +146,7 @@ extension CoreSdkClient { typealias MediaType = SalemoveSDK.MediaType typealias MediaUgradeOfferBlock = SalemoveSDK.MediaUgradeOfferBlock typealias MediaUpgradeOffer = SalemoveSDK.MediaUpgradeOffer + typealias MediaUpdateBlock = SalemoveSDK.MediaUpdateBlock typealias Message = SalemoveSDK.Message typealias MessageSender = SalemoveSDK.MessageSender typealias MessageBlock = SalemoveSDK.MessageBlock @@ -177,5 +178,4 @@ extension CoreSdkClient { typealias Site = SalemoveSDK.Site typealias Survey = SalemoveSDK.Survey typealias SurveyAnswerContainer = SalemoveSDK.Survey.Answer.ValueContainer - typealias MediaUpdateBlock = SalemoveSDK.MediaUpdateBlock } diff --git a/GliaWidgets/GCD.Live.swift b/GliaWidgets/GCD.Live.swift index 5564bed87..fb56f1bfb 100644 --- a/GliaWidgets/GCD.Live.swift +++ b/GliaWidgets/GCD.Live.swift @@ -1,4 +1,5 @@ import Dispatch +import Foundation extension GCD { static let live = Self( diff --git a/GliaWidgets/Interactor/Interactor.swift b/GliaWidgets/Interactor/Interactor.swift index 3f5044835..65a8cb862 100644 --- a/GliaWidgets/Interactor/Interactor.swift +++ b/GliaWidgets/Interactor/Interactor.swift @@ -20,6 +20,7 @@ enum InteractorEvent { case messagesUpdated([CoreSdkClient.Message]) case typingStatusUpdated(CoreSdkClient.OperatorTypingStatus) case upgradeOffer(CoreSdkClient.MediaUpgradeOffer, answer: CoreSdkClient.AnswerWithSuccessBlock) + case updateOffer(CoreSdkClient.MediaUpgradeOffer) case audioStreamAdded(CoreSdkClient.AudioStreamable) case audioStreamError(CoreSdkClient.SalemoveError) case videoStreamAdded(CoreSdkClient.VideoStreamable) @@ -244,12 +245,6 @@ extension Interactor { } extension Interactor: CoreSdkClient.Interactable { - var onEngagementTransferMediaUpdate: CoreSdkClient.MediaUpdateBlock { - { _ in - - } - } - var onScreenSharingOffer: CoreSdkClient.ScreenshareOfferBlock { return { [weak self] answer in self?.notify(.screenShareOffer(answer: answer)) @@ -262,6 +257,12 @@ extension Interactor: CoreSdkClient.Interactable { } } + var onEngagementTransferMediaUpdate: CoreSdkClient.MediaUpdateBlock { + return { [weak self] offer in + self?.notify(.updateOffer(offer)) + } + } + var onEngagementRequest: CoreSdkClient.RequestOfferBlock { return { [weak self] answer in answer(self?.visitorContext, true) { _, _ in } diff --git a/GliaWidgets/Lib/Upload/FileUpload.Environment.Interface.swift b/GliaWidgets/Lib/Upload/FileUpload.Environment.Interface.swift index 327e141e0..8daac98dd 100644 --- a/GliaWidgets/Lib/Upload/FileUpload.Environment.Interface.swift +++ b/GliaWidgets/Lib/Upload/FileUpload.Environment.Interface.swift @@ -1,3 +1,5 @@ +import Foundation + extension FileUpload { struct Environment { var uploadFileToEngagement: CoreSdkClient.UploadFileToEngagement diff --git a/GliaWidgets/ViewModel/Call/CallViewModel.swift b/GliaWidgets/ViewModel/Call/CallViewModel.swift index 479ab7743..a22a20006 100644 --- a/GliaWidgets/ViewModel/Call/CallViewModel.swift +++ b/GliaWidgets/ViewModel/Call/CallViewModel.swift @@ -256,6 +256,8 @@ class CallViewModel: EngagementViewModel, ViewModel { handleVideoStreamError(error) case .upgradeOffer(let offer, answer: let answer): offerMediaUpgrade(offer, answer: answer) + case .updateOffer(let offer): + call.upgrade(to: offer) case .engagementTransferring: onEngagementTransferring() default: @@ -266,6 +268,7 @@ class CallViewModel: EngagementViewModel, ViewModel { extension CallViewModel { private func onEngagementTransferring() { + endScreenSharing() call.transfer() durationCounter.stop() action?(.transferring) diff --git a/GliaWidgetsTests/Sources/CallViewModelTests.swift b/GliaWidgetsTests/Sources/CallViewModelTests.swift index 847754e6d..d30b89c86 100644 --- a/GliaWidgetsTests/Sources/CallViewModelTests.swift +++ b/GliaWidgetsTests/Sources/CallViewModelTests.swift @@ -2,10 +2,12 @@ import XCTest @testable import GliaWidgets +// swiftlint:disable type_body_length class CallViewModelTests: XCTestCase { var viewModel: CallViewModel! var call: Call! + // swiftlint:disable function_body_length func test_setCallOnHoldPausesLocalVideoAndMutesLocalAudio() throws { var isVideoButtonEnabled: Bool = true var isMuteButtonEnabled: Bool = true @@ -36,7 +38,7 @@ class CallViewModelTests: XCTestCase { case .mute: isMuteButtonEnabled = isEnabled - + default: break } @@ -53,7 +55,7 @@ class CallViewModelTests: XCTestCase { getIsRemoteFunc: { true }, setIsRemoteFunc: { _ in } ) - + let remoteVideoStream = CoreSdkClient.MockVideoStreamable.mock( getStreamViewFunc: { .init() }, playVideoFunc: {}, @@ -74,7 +76,7 @@ class CallViewModelTests: XCTestCase { getIsRemoteFunc: { false }, setIsRemoteFunc: { _ in } ) - + let localVideoStream = CoreSdkClient.MockVideoStreamable.mock( getStreamViewFunc: { .init() }, playVideoFunc: {}, @@ -86,7 +88,7 @@ class CallViewModelTests: XCTestCase { getIsRemoteFunc: { false }, setIsRemoteFunc: { _ in } ) - + call.updateAudioStream(with: remoteAudioStream) call.updateVideoStream(with: remoteVideoStream) call.updateAudioStream(with: localAudioStream) @@ -100,6 +102,7 @@ class CallViewModelTests: XCTestCase { XCTAssertTrue(isLocalAudioStreamMuted) } + // swiftlint:disable function_body_length func test_toggleCallOnHoldRestoresPreviousLocalVideoAndAudioState() throws { var isLocalAudioStreamMuted: Bool = false var isLocalVideoStreamPaused: Bool = false @@ -127,7 +130,7 @@ class CallViewModelTests: XCTestCase { getIsRemoteFunc: { true }, setIsRemoteFunc: { _ in } ) - + let remoteVideoStream = CoreSdkClient.MockVideoStreamable.mock( getStreamViewFunc: { .init() }, playVideoFunc: {}, @@ -148,7 +151,7 @@ class CallViewModelTests: XCTestCase { getIsRemoteFunc: { false }, setIsRemoteFunc: { _ in } ) - + let localVideoStream = CoreSdkClient.MockVideoStreamable.mock( getStreamViewFunc: { .init() }, playVideoFunc: {}, @@ -160,7 +163,7 @@ class CallViewModelTests: XCTestCase { getIsRemoteFunc: { false }, setIsRemoteFunc: { _ in } ) - + call.updateAudioStream(with: remoteAudioStream) call.updateVideoStream(with: remoteVideoStream) call.updateAudioStream(with: localAudioStream) @@ -181,7 +184,7 @@ class CallViewModelTests: XCTestCase { XCTAssertTrue(isLocalAudioStreamMuted) XCTAssertTrue(isLocalVideoStreamPaused) } - + func test_engagementTransferringReleasesRemoteAndLocalVideoAndShowsConnectingState() throws { enum Calls { case showConnecting } var calls: [Calls] = [] @@ -209,10 +212,10 @@ class CallViewModelTests: XCTestCase { switch action { case .connecting: calls.append(.showConnecting) - + case .setRemoteVideo(let video): XCTAssertNil(video) - + case .setLocalVideo(let video): XCTAssertNil(video) @@ -225,7 +228,8 @@ class CallViewModelTests: XCTestCase { XCTAssertEqual([.showConnecting], calls) } - + + // swiftlint:disable function_body_length func test_engagementTransferringReleasesStreams() throws { var interactorEnv: Interactor.Environment = .failing interactorEnv.gcd.mainQueue.asyncIfNeeded = { $0() } @@ -294,7 +298,7 @@ class CallViewModelTests: XCTestCase { XCTAssertFalse(call.audio.stream.value.remoteStream == nil) XCTAssertFalse(call.video.stream.value.localStream == nil) XCTAssertFalse(call.video.stream.value.remoteStream == nil) - + interactor.notify(.engagementTransferring) XCTAssertNil(call.video.stream.value.localStream) @@ -302,4 +306,33 @@ class CallViewModelTests: XCTestCase { XCTAssertNil(call.audio.stream.value.localStream) XCTAssertNil(call.audio.stream.value.remoteStream) } + + func test_interactorEventUpdatesCallMediaState() throws { + var interactorEnv: Interactor.Environment = .failing + interactorEnv.gcd.mainQueue.asyncIfNeeded = { $0() } + let interactor: Interactor = .mock(environment: interactorEnv) + + let offer = try CoreSdkClient.MediaUpgradeOffer(type: .audio, direction: .oneWay) + + call = .init( + .video(direction: .twoWay), + environment: .mock + ) + + viewModel = .init( + interactor: interactor, + alertConfiguration: .mock(), + screenShareHandler: ScreenShareHandler(), + environment: .mock, + call: call, + unreadMessages: .init(with: 0), + startWith: .engagement(mediaType: .video) + ) + + XCTAssertEqual(call.kind.value, .video(direction: .twoWay)) + + interactor.notify(.updateOffer(offer)) + + XCTAssertEqual(call.kind.value, .audio) + } }