From fc5c6f87753c9c1b89be463590cdc5def2ae349b Mon Sep 17 00:00:00 2001 From: Egor Egorov Date: Thu, 12 Oct 2023 16:28:25 +0300 Subject: [PATCH] Fix ending CV engagement Previously if WidgetsSDK is configured one time and then visitor has regular engagement, then CV engagement with screen sharing, if the operator ends CV engagement, bubble and screen sharing state is still displayed. It happened because EngagementViewModel called interator.endSession which set `isEngagementEndedByVisitor` to `true` even in case if engagement is ended by operator. Since CV engagement is started without additional `configure` call, interactor keeps `isEngagementEndedByVisitor` as `true`, which was broking CV flow. This commit fixes it. MOB-2731 --- GliaWidgets.xcodeproj/project.pbxproj | 4 +++ GliaWidgets/Public/Glia/Glia.swift | 2 +- .../Sources/Interactor/Interactor.swift | 17 +++++++---- .../ChatViewModel/Mocks/Survey.Mock.swift | 24 +++++++++++++++ GliaWidgetsTests/Sources/Glia/GliaTests.swift | 30 +++++++++++++++++++ .../Sources/InteractorTests.swift | 30 +++++++++++++++++++ 6 files changed, 101 insertions(+), 6 deletions(-) create mode 100644 GliaWidgetsTests/Sources/ChatViewModel/Mocks/Survey.Mock.swift diff --git a/GliaWidgets.xcodeproj/project.pbxproj b/GliaWidgets.xcodeproj/project.pbxproj index 067669301..18d20f15e 100644 --- a/GliaWidgets.xcodeproj/project.pbxproj +++ b/GliaWidgets.xcodeproj/project.pbxproj @@ -351,6 +351,7 @@ 845E2F9D283FCB1400C04D56 /* Theme.Survey.Checkbox.Accessibility.swift in Sources */ = {isa = PBXBuildFile; fileRef = 845E2F9C283FCB1400C04D56 /* Theme.Survey.Checkbox.Accessibility.swift */; }; 84602A742AE94DE50031E606 /* ProximityManager.Mock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84602A732AE94DE50031E606 /* ProximityManager.Mock.swift */; }; 84602A772AEA5BEA0031E606 /* ProximityManager.Failing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84602A762AEA5BEA0031E606 /* ProximityManager.Failing.swift */; }; + 84602A792AEAB7CA0031E606 /* Survey.Mock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84602A782AEAB7CA0031E606 /* Survey.Mock.swift */; }; 8464297A2A44937600943BD6 /* AlertViewControllerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 846429792A44937600943BD6 /* AlertViewControllerTests.swift */; }; 8464297D2A459E7D00943BD6 /* UIViewController+Replaceble.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8464297C2A459E7D00943BD6 /* UIViewController+Replaceble.swift */; }; 846429802A45A1F200943BD6 /* OfferPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8464297F2A45A1F200943BD6 /* OfferPresenter.swift */; }; @@ -1093,6 +1094,7 @@ 845E2F9C283FCB1400C04D56 /* Theme.Survey.Checkbox.Accessibility.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Theme.Survey.Checkbox.Accessibility.swift; sourceTree = ""; }; 84602A732AE94DE50031E606 /* ProximityManager.Mock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProximityManager.Mock.swift; sourceTree = ""; }; 84602A762AEA5BEA0031E606 /* ProximityManager.Failing.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProximityManager.Failing.swift; sourceTree = ""; }; + 84602A782AEAB7CA0031E606 /* Survey.Mock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Survey.Mock.swift; sourceTree = ""; }; 846429792A44937600943BD6 /* AlertViewControllerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlertViewControllerTests.swift; sourceTree = ""; }; 8464297C2A459E7D00943BD6 /* UIViewController+Replaceble.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIViewController+Replaceble.swift"; sourceTree = ""; }; 8464297F2A45A1F200943BD6 /* OfferPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OfferPresenter.swift; sourceTree = ""; }; @@ -3180,6 +3182,7 @@ isa = PBXGroup; children = ( 84681A972A61853300DD7406 /* GvaOption.Mock.swift */, + 84602A782AEAB7CA0031E606 /* Survey.Mock.swift */, ); path = Mocks; sourceTree = ""; @@ -4940,6 +4943,7 @@ 846429832A45DA7500943BD6 /* AlertViewController+Mock.swift in Sources */, 846A5C4529F6BEFA0049B29F /* GliaTests+StartEngagement.swift in Sources */, 7512A57A27BF9FCD00319DF1 /* ChatViewModelTests.swift in Sources */, + 84602A792AEAB7CA0031E606 /* Survey.Mock.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/GliaWidgets/Public/Glia/Glia.swift b/GliaWidgets/Public/Glia/Glia.swift index b6249ada2..31ce3b2f6 100644 --- a/GliaWidgets/Public/Glia/Glia.swift +++ b/GliaWidgets/Public/Glia/Glia.swift @@ -338,7 +338,7 @@ extension Glia { case let .videoStreamAdded(stream): self?.callVisualizer.addVideoStream(stream: stream) case let .stateChanged(state): - if state == .ended(.byOperator) { + if case .ended = state { self?.callVisualizer.endSession() self?.onEvent?(.ended) } else if case .engaged = state { diff --git a/GliaWidgets/Sources/Interactor/Interactor.swift b/GliaWidgets/Sources/Interactor/Interactor.swift index 7873b4d39..207d864a0 100644 --- a/GliaWidgets/Sources/Interactor/Interactor.swift +++ b/GliaWidgets/Sources/Interactor/Interactor.swift @@ -59,7 +59,6 @@ class Interactor { var currentEngagement: CoreSdkClient.Engagement? private var observers = [() -> (AnyObject?, EventHandler)]() - private var isEngagementEndedByVisitor = false var state: InteractorState = .none { didSet { @@ -159,8 +158,6 @@ extension Interactor { success: @escaping () -> Void, failure: @escaping (CoreSdkClient.SalemoveError) -> Void ) { - isEngagementEndedByVisitor = true - switch state { case .none: success() @@ -203,10 +200,11 @@ extension Interactor { success: @escaping () -> Void, failure: @escaping (CoreSdkClient.SalemoveError) -> Void ) { - environment.coreSdk.endEngagement { _, error in + environment.coreSdk.endEngagement { [weak self] _, error in if let error = error { failure(error) } else { + self?.state = .ended(.byVisitor) success() } } @@ -335,7 +333,16 @@ extension Interactor: CoreSdkClient.Interactable { func end(with reason: CoreSdkClient.EngagementEndingReason) { currentEngagement = environment.coreSdk.getCurrentEngagement() - state = isEngagementEndedByVisitor == true ? .ended(.byVisitor) : .ended(.byOperator) + switch reason { + case .visitorHungUp: + state = .ended(.byVisitor) + case .operatorHungUp: + state = .ended(.byOperator) + case .error: + state = .ended(.byError) + @unknown default: + state = .ended(.byError) + } } func fail(error: CoreSdkClient.SalemoveError) { diff --git a/GliaWidgetsTests/Sources/ChatViewModel/Mocks/Survey.Mock.swift b/GliaWidgetsTests/Sources/ChatViewModel/Mocks/Survey.Mock.swift new file mode 100644 index 000000000..2c73f171c --- /dev/null +++ b/GliaWidgetsTests/Sources/ChatViewModel/Mocks/Survey.Mock.swift @@ -0,0 +1,24 @@ +import Foundation +@testable import GliaWidgets + +extension CoreSdkClient.Survey { + static func mock( + id: String = "mock", + description: String = "mock", + name: String = "mock", + title: String = "mock", + type: String = "visitor", + siteId: String = "mock" + ) throws -> Self { + let data = try JSONSerialization.data(withJSONObject: [ + "id": id, + "description": description, + "name": name, + "title": title, + "type": type, + "siteId": siteId, + "questions": [] + ]) + return try JSONDecoder().decode(CoreSdkClient.Survey.self, from: data) + } +} diff --git a/GliaWidgetsTests/Sources/Glia/GliaTests.swift b/GliaWidgetsTests/Sources/Glia/GliaTests.swift index 519fe5bc3..e4e3e83ef 100644 --- a/GliaWidgetsTests/Sources/Glia/GliaTests.swift +++ b/GliaWidgetsTests/Sources/Glia/GliaTests.swift @@ -353,4 +353,34 @@ final class GliaTests: XCTestCase { XCTAssertNil(sdk.rootCoordinator) XCTAssertNil(rootCoordinator) } + + func test_screenSharingIsStoppedWhenCallVisualizerEngagementIsEnded() throws { + enum Call { case ended } + var calls: [Call] = [] + var gliaEnv = Glia.Environment.failing + let screenShareHandler: ScreenShareHandler = .mock + screenShareHandler.status().value = .started + gliaEnv.screenShareHandler = screenShareHandler + gliaEnv.gcd.mainQueue.asyncIfNeeded = { callback in callback() } + gliaEnv.coreSDKConfigurator.configureWithConfiguration = { _, callback in + callback?() + } + gliaEnv.coreSDKConfigurator.configureWithInteractor = { _ in } + let sdk = Glia(environment: gliaEnv) + try sdk.configure(with: .mock()) {} + sdk.callVisualizer.delegate?(.visitorCodeIsRequested) + sdk.environment.coreSdk.getCurrentEngagement = { .mock(source: .callVisualizer) } + sdk.onEvent = { event in + switch event { + case .ended: + calls.append(.ended) + default: + XCTFail("There is should be no another event") + } + } + sdk.interactor?.state = .ended(.byVisitor) + + XCTAssertEqual(screenShareHandler.status().value, .stopped) + XCTAssertEqual(calls, [.ended]) + } } diff --git a/GliaWidgetsTests/Sources/InteractorTests.swift b/GliaWidgetsTests/Sources/InteractorTests.swift index 78f3d0510..c91c880e2 100644 --- a/GliaWidgetsTests/Sources/InteractorTests.swift +++ b/GliaWidgetsTests/Sources/InteractorTests.swift @@ -447,4 +447,34 @@ class InteractorTests: XCTestCase { XCTAssertEqual(callbacks, [.mediaUpgradeOffered]) } + + func test_endEngagementSetsStateToEndedByVisitor() { + var interactorEnv = Interactor.Environment.failing + interactorEnv.coreSdk.endEngagement = { completion in completion(true, nil) } + let interactor = Interactor.mock(environment: interactorEnv) + interactor.state = .engaged(.mock()) + + interactor.endSession(success: {}, failure: { _ in }) + + XCTAssertEqual(interactor.state, .ended(.byVisitor)) + } + + func test_endWithReasonSetsProperState() { + let interactor = Interactor.failing + interactor.state = .engaged(.mock()) + typealias Item = (reason: CoreSdkClient.EngagementEndingReason, state: InteractorState) + + let items: [Item] = [ + (.visitorHungUp, .ended(.byVisitor)), + (.operatorHungUp, .ended(.byOperator)), + (.error, .ended(.byError)) + ] + + let test: (Item) -> Void = { item in + interactor.end(with: item.reason) + XCTAssertEqual(interactor.state, item.state) + } + + items.forEach(test) + } }