mirror of
https://github.com/element-hq/element-x-ios.git
synced 2025-03-10 21:39:12 +00:00
Check Application state before sending RR (#1960)
* check the state * better handling * protocol to mock the application state * test improvement
This commit is contained in:
parent
af76be2426
commit
d457736673
@ -634,6 +634,7 @@
|
||||
A722F426FD81FC67706BB1E0 /* CustomLayoutLabelStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42236480CF0431535EBE8387 /* CustomLayoutLabelStyle.swift */; };
|
||||
A743841F91B62B0E56217B04 /* SecureBackupKeyBackupScreenUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58DCB219D7B7B0299358FF81 /* SecureBackupKeyBackupScreenUITests.swift */; };
|
||||
A74438ED16F8683A4B793E6A /* AnalyticsSettingsScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0BCE3FAF40932AC7C7639AC4 /* AnalyticsSettingsScreenViewModel.swift */; };
|
||||
A7BACE682AE97D4500FFBBEA /* ApplicationMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = A7BACE672AE97D4500FFBBEA /* ApplicationMock.swift */; };
|
||||
A7D48E44D485B143AADDB77D /* Strings+Untranslated.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A18F6CE4D694D21E4EA9B25 /* Strings+Untranslated.swift */; };
|
||||
A7FD7B992E6EE6E5A8429197 /* RoomSummaryDetails.swift in Sources */ = {isa = PBXBuildFile; fileRef = 142808B69851451AC32A2CEA /* RoomSummaryDetails.swift */; };
|
||||
A816F7087C495D85048AC50E /* RoomMemberDetailsScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1B6E30BB748F3F480F077969 /* RoomMemberDetailsScreenModels.swift */; };
|
||||
@ -1030,7 +1031,7 @@
|
||||
033DB41C51865A2E83174E87 /* target.yml */ = {isa = PBXFileReference; lastKnownFileType = text.yaml; path = target.yml; sourceTree = "<group>"; };
|
||||
035177BCD8E8308B098AC3C2 /* WindowManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WindowManager.swift; sourceTree = "<group>"; };
|
||||
0376C429FAB1687C3D905F3E /* MockCoder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockCoder.swift; sourceTree = "<group>"; };
|
||||
0392E3FDE372C9B56FEEED8B /* test_voice_message.m4a */ = {isa = PBXFileReference; path = test_voice_message.m4a; sourceTree = "<group>"; };
|
||||
0392E3FDE372C9B56FEEED8B /* test_voice_message.m4a */ = {isa = PBXFileReference; lastKnownFileType = file; path = test_voice_message.m4a; sourceTree = "<group>"; };
|
||||
03BA7958A4BB9C22CA8884EF /* WaveformViewDragGestureModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WaveformViewDragGestureModifier.swift; sourceTree = "<group>"; };
|
||||
03DD998E523D4EC93C7ED703 /* RoomNotificationSettingsScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomNotificationSettingsScreenViewModelProtocol.swift; sourceTree = "<group>"; };
|
||||
03FABD73FD8086EFAB699F42 /* MediaUploadPreviewScreenViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaUploadPreviewScreenViewModelTests.swift; sourceTree = "<group>"; };
|
||||
@ -1585,6 +1586,7 @@
|
||||
A6B891A6DA826E2461DBB40F /* PHGPostHogConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PHGPostHogConfiguration.swift; sourceTree = "<group>"; };
|
||||
A6C11AD9813045E44F950410 /* ElementCallWidgetDriverProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ElementCallWidgetDriverProtocol.swift; sourceTree = "<group>"; };
|
||||
A73A07BAEDD74C48795A996A /* AsyncSequence.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AsyncSequence.swift; sourceTree = "<group>"; };
|
||||
A7BACE672AE97D4500FFBBEA /* ApplicationMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApplicationMock.swift; sourceTree = "<group>"; };
|
||||
A7C4EA55DA62F9D0F984A2AE /* CollapsibleTimelineItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CollapsibleTimelineItem.swift; sourceTree = "<group>"; };
|
||||
A861DA5932B128FE1DCB5CE2 /* InviteUsersScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InviteUsersScreenCoordinator.swift; sourceTree = "<group>"; };
|
||||
A8903A9F615BBD0E6D7CD133 /* ApplicationProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApplicationProtocol.swift; sourceTree = "<group>"; };
|
||||
@ -2439,6 +2441,7 @@
|
||||
AAD01F7FC2BBAC7351948595 /* UserProfile+Mock.swift */,
|
||||
B23135B06B044CB811139D2F /* Generated */,
|
||||
E5E545F92D01588360A9BAC5 /* SDK */,
|
||||
A7BACE672AE97D4500FFBBEA /* ApplicationMock.swift */,
|
||||
);
|
||||
path = Mocks;
|
||||
sourceTree = "<group>";
|
||||
@ -5289,6 +5292,7 @@
|
||||
0F6C8033FA60CFD36F7CA205 /* AppLockSetupPINScreenViewModel.swift in Sources */,
|
||||
4C356F5CCB4CDC99BFA45185 /* AppLockSetupPINScreenViewModelProtocol.swift in Sources */,
|
||||
4E8A2A2CFEB212F14E49E1A1 /* AppLockSetupSettingsScreen.swift in Sources */,
|
||||
A7BACE682AE97D4500FFBBEA /* ApplicationMock.swift in Sources */,
|
||||
8DCD9CC5361FF22A5B2C20F1 /* AppLockSetupSettingsScreenCoordinator.swift in Sources */,
|
||||
6E4E401BE97AC241DA7C7716 /* AppLockSetupSettingsScreenModels.swift in Sources */,
|
||||
4807E8F51DB54F56B25E1C7E /* AppLockSetupSettingsScreenViewModel.swift in Sources */,
|
||||
|
73
ElementX/Sources/Mocks/ApplicationMock.swift
Normal file
73
ElementX/Sources/Mocks/ApplicationMock.swift
Normal file
@ -0,0 +1,73 @@
|
||||
//
|
||||
// Copyright 2023 New Vector Ltd
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
extension ApplicationMock {
|
||||
static var `default`: ApplicationProtocol {
|
||||
ApplicationMock(withState: .active,
|
||||
backgroundTimeRemaining: 10,
|
||||
allowTasks: true)
|
||||
}
|
||||
|
||||
static var mockBroken: ApplicationProtocol {
|
||||
ApplicationMock(withState: .inactive,
|
||||
backgroundTimeRemaining: 0,
|
||||
allowTasks: false)
|
||||
}
|
||||
|
||||
static var mockAboutToSuspend: ApplicationProtocol {
|
||||
ApplicationMock(withState: .background,
|
||||
backgroundTimeRemaining: 2,
|
||||
allowTasks: false)
|
||||
}
|
||||
|
||||
private static var bgTaskIdentifier = 0
|
||||
|
||||
convenience init(withState applicationState: UIApplication.State,
|
||||
backgroundTimeRemaining: TimeInterval,
|
||||
allowTasks: Bool) {
|
||||
self.init()
|
||||
|
||||
underlyingApplicationState = applicationState
|
||||
underlyingBackgroundTimeRemaining = backgroundTimeRemaining
|
||||
|
||||
beginBackgroundTaskExpirationHandlerClosure = { [weak self] handler in
|
||||
guard let self else {
|
||||
return .invalid
|
||||
}
|
||||
|
||||
guard allowTasks else {
|
||||
return .invalid
|
||||
}
|
||||
|
||||
return beginBackgroundTask(withName: nil, expirationHandler: handler)
|
||||
}
|
||||
|
||||
beginBackgroundTaskWithNameExpirationHandlerClosure = { _, handler in
|
||||
guard allowTasks else {
|
||||
return .invalid
|
||||
}
|
||||
Self.bgTaskIdentifier += 1
|
||||
|
||||
let identifier = UIBackgroundTaskIdentifier(rawValue: Self.bgTaskIdentifier)
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
|
||||
handler?()
|
||||
}
|
||||
return identifier
|
||||
}
|
||||
}
|
||||
}
|
@ -279,6 +279,85 @@ class AppLockServiceMock: AppLockServiceProtocol {
|
||||
}
|
||||
}
|
||||
}
|
||||
class ApplicationMock: ApplicationProtocol {
|
||||
var backgroundTimeRemaining: TimeInterval {
|
||||
get { return underlyingBackgroundTimeRemaining }
|
||||
set(value) { underlyingBackgroundTimeRemaining = value }
|
||||
}
|
||||
var underlyingBackgroundTimeRemaining: TimeInterval!
|
||||
var applicationState: UIApplication.State {
|
||||
get { return underlyingApplicationState }
|
||||
set(value) { underlyingApplicationState = value }
|
||||
}
|
||||
var underlyingApplicationState: UIApplication.State!
|
||||
|
||||
//MARK: - beginBackgroundTask
|
||||
|
||||
var beginBackgroundTaskExpirationHandlerCallsCount = 0
|
||||
var beginBackgroundTaskExpirationHandlerCalled: Bool {
|
||||
return beginBackgroundTaskExpirationHandlerCallsCount > 0
|
||||
}
|
||||
var beginBackgroundTaskExpirationHandlerReturnValue: UIBackgroundTaskIdentifier!
|
||||
var beginBackgroundTaskExpirationHandlerClosure: (((() -> Void)?) -> UIBackgroundTaskIdentifier)?
|
||||
|
||||
func beginBackgroundTask(expirationHandler handler: (() -> Void)?) -> UIBackgroundTaskIdentifier {
|
||||
beginBackgroundTaskExpirationHandlerCallsCount += 1
|
||||
if let beginBackgroundTaskExpirationHandlerClosure = beginBackgroundTaskExpirationHandlerClosure {
|
||||
return beginBackgroundTaskExpirationHandlerClosure(handler)
|
||||
} else {
|
||||
return beginBackgroundTaskExpirationHandlerReturnValue
|
||||
}
|
||||
}
|
||||
//MARK: - beginBackgroundTask
|
||||
|
||||
var beginBackgroundTaskWithNameExpirationHandlerCallsCount = 0
|
||||
var beginBackgroundTaskWithNameExpirationHandlerCalled: Bool {
|
||||
return beginBackgroundTaskWithNameExpirationHandlerCallsCount > 0
|
||||
}
|
||||
var beginBackgroundTaskWithNameExpirationHandlerReturnValue: UIBackgroundTaskIdentifier!
|
||||
var beginBackgroundTaskWithNameExpirationHandlerClosure: ((String?, (() -> Void)?) -> UIBackgroundTaskIdentifier)?
|
||||
|
||||
func beginBackgroundTask(withName taskName: String?, expirationHandler handler: (() -> Void)?) -> UIBackgroundTaskIdentifier {
|
||||
beginBackgroundTaskWithNameExpirationHandlerCallsCount += 1
|
||||
if let beginBackgroundTaskWithNameExpirationHandlerClosure = beginBackgroundTaskWithNameExpirationHandlerClosure {
|
||||
return beginBackgroundTaskWithNameExpirationHandlerClosure(taskName, handler)
|
||||
} else {
|
||||
return beginBackgroundTaskWithNameExpirationHandlerReturnValue
|
||||
}
|
||||
}
|
||||
//MARK: - endBackgroundTask
|
||||
|
||||
var endBackgroundTaskCallsCount = 0
|
||||
var endBackgroundTaskCalled: Bool {
|
||||
return endBackgroundTaskCallsCount > 0
|
||||
}
|
||||
var endBackgroundTaskReceivedIdentifier: UIBackgroundTaskIdentifier?
|
||||
var endBackgroundTaskReceivedInvocations: [UIBackgroundTaskIdentifier] = []
|
||||
var endBackgroundTaskClosure: ((UIBackgroundTaskIdentifier) -> Void)?
|
||||
|
||||
func endBackgroundTask(_ identifier: UIBackgroundTaskIdentifier) {
|
||||
endBackgroundTaskCallsCount += 1
|
||||
endBackgroundTaskReceivedIdentifier = identifier
|
||||
endBackgroundTaskReceivedInvocations.append(identifier)
|
||||
endBackgroundTaskClosure?(identifier)
|
||||
}
|
||||
//MARK: - open
|
||||
|
||||
var openCallsCount = 0
|
||||
var openCalled: Bool {
|
||||
return openCallsCount > 0
|
||||
}
|
||||
var openReceivedUrl: URL?
|
||||
var openReceivedInvocations: [URL] = []
|
||||
var openClosure: ((URL) -> Void)?
|
||||
|
||||
func open(_ url: URL) {
|
||||
openCallsCount += 1
|
||||
openReceivedUrl = url
|
||||
openReceivedInvocations.append(url)
|
||||
openClosure?(url)
|
||||
}
|
||||
}
|
||||
class AudioConverterMock: AudioConverterProtocol {
|
||||
|
||||
//MARK: - convertToOpusOgg
|
||||
|
@ -65,7 +65,8 @@ final class RoomScreenCoordinator: CoordinatorProtocol {
|
||||
roomProxy: parameters.roomProxy,
|
||||
appSettings: parameters.appSettings,
|
||||
analytics: ServiceLocator.shared.analytics,
|
||||
userIndicatorController: ServiceLocator.shared.userIndicatorController)
|
||||
userIndicatorController: ServiceLocator.shared.userIndicatorController,
|
||||
application: UIApplication.shared)
|
||||
|
||||
wysiwygViewModel = WysiwygComposerViewModel(minHeight: ComposerConstant.minHeight,
|
||||
maxCompressedHeight: ComposerConstant.maxHeight,
|
||||
|
@ -32,6 +32,7 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol
|
||||
private let roomProxy: RoomProxyProtocol
|
||||
private let appSettings: AppSettings
|
||||
private let analytics: AnalyticsService
|
||||
private let application: ApplicationProtocol
|
||||
private unowned let userIndicatorController: UserIndicatorControllerProtocol
|
||||
private let notificationCenterProtocol: NotificationCenterProtocol
|
||||
private let voiceMessageRecorder: VoiceMessageRecorderProtocol
|
||||
@ -48,6 +49,7 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol
|
||||
appSettings: AppSettings,
|
||||
analytics: AnalyticsService,
|
||||
userIndicatorController: UserIndicatorControllerProtocol,
|
||||
application: ApplicationProtocol,
|
||||
notificationCenterProtocol: NotificationCenterProtocol = NotificationCenter.default) {
|
||||
self.roomProxy = roomProxy
|
||||
self.timelineController = timelineController
|
||||
@ -56,6 +58,7 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol
|
||||
self.userIndicatorController = userIndicatorController
|
||||
self.notificationCenterProtocol = notificationCenterProtocol
|
||||
self.mediaPlayerProvider = mediaPlayerProvider
|
||||
self.application = application
|
||||
voiceMessageRecorder = VoiceMessageRecorder(audioRecorder: AudioRecorder(), mediaPlayerProvider: mediaPlayerProvider)
|
||||
|
||||
super.init(initialViewState: RoomScreenViewState(roomID: timelineController.roomID,
|
||||
@ -327,16 +330,19 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol
|
||||
/// The ID of the newest item in the room that the user has seen.
|
||||
/// This includes both event based items and virtual items.
|
||||
private var lastReadItemID: TimelineItemIdentifier?
|
||||
private func sendReadReceiptIfNeeded(for lastVisibleItemID: TimelineItemIdentifier) async -> Result<Void, RoomTimelineControllerError> {
|
||||
private func sendReadReceiptIfNeeded(for lastVisibleItemID: TimelineItemIdentifier) async {
|
||||
guard application.applicationState == .active else { return }
|
||||
|
||||
guard lastReadItemID != lastVisibleItemID,
|
||||
let eventItemID = eventBasedItem(nearest: lastVisibleItemID)
|
||||
else { return .success(()) }
|
||||
let eventItemID = eventBasedItem(nearest: lastVisibleItemID) else {
|
||||
return
|
||||
}
|
||||
|
||||
// Make sure the item is newer than the item that was last marked as read.
|
||||
if let lastReadItemIndex = state.timelineViewState.timelineIDs.firstIndex(of: lastReadItemID?.timelineID ?? ""),
|
||||
let lastVisibleItemIndex = state.timelineViewState.timelineIDs.firstIndex(of: eventItemID.timelineID),
|
||||
lastReadItemIndex > lastVisibleItemIndex {
|
||||
return .success(())
|
||||
return
|
||||
}
|
||||
|
||||
// Update the last read item ID to avoid attempting duplicate requests.
|
||||
@ -348,10 +354,10 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol
|
||||
}
|
||||
|
||||
switch await timelineController.sendReadReceipt(for: eventItemID) {
|
||||
case .success:
|
||||
return .success(())
|
||||
case .failure:
|
||||
return .failure(.generic)
|
||||
case .success():
|
||||
break
|
||||
case let .failure(error):
|
||||
MXLog.error("[TimelineViewController] Failed to send read receipt: \(error)")
|
||||
}
|
||||
}
|
||||
|
||||
@ -1028,7 +1034,7 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol
|
||||
|
||||
private func openSystemSettings() {
|
||||
guard let url = URL(string: UIApplication.openSettingsURLString) else { return }
|
||||
UIApplication.shared.open(url)
|
||||
application.open(url)
|
||||
}
|
||||
}
|
||||
|
||||
@ -1061,7 +1067,8 @@ extension RoomScreenViewModel {
|
||||
roomProxy: RoomProxyMock(with: .init(displayName: "Preview room")),
|
||||
appSettings: ServiceLocator.shared.settings,
|
||||
analytics: ServiceLocator.shared.analytics,
|
||||
userIndicatorController: ServiceLocator.shared.userIndicatorController)
|
||||
userIndicatorController: ServiceLocator.shared.userIndicatorController,
|
||||
application: ApplicationMock.default)
|
||||
}
|
||||
|
||||
private struct ReplyInfo {
|
||||
|
@ -61,7 +61,8 @@ struct RoomHeaderView_Previews: PreviewProvider, TestablePreview {
|
||||
roomProxy: RoomProxyMock(with: .init(displayName: "Some Room name", avatarURL: URL.picturesDirectory)),
|
||||
appSettings: ServiceLocator.shared.settings,
|
||||
analytics: ServiceLocator.shared.analytics,
|
||||
userIndicatorController: ServiceLocator.shared.userIndicatorController)
|
||||
userIndicatorController: ServiceLocator.shared.userIndicatorController,
|
||||
application: ApplicationMock.default)
|
||||
|
||||
RoomHeaderView(context: viewModel.context)
|
||||
.previewLayout(.sizeThatFits)
|
||||
@ -76,7 +77,8 @@ struct RoomHeaderView_Previews: PreviewProvider, TestablePreview {
|
||||
roomProxy: RoomProxyMock(with: .init(displayName: "Some Room name")),
|
||||
appSettings: ServiceLocator.shared.settings,
|
||||
analytics: ServiceLocator.shared.analytics,
|
||||
userIndicatorController: ServiceLocator.shared.userIndicatorController)
|
||||
userIndicatorController: ServiceLocator.shared.userIndicatorController,
|
||||
application: ApplicationMock.default)
|
||||
|
||||
RoomHeaderView(context: viewModel.context)
|
||||
.previewLayout(.sizeThatFits)
|
||||
|
@ -195,7 +195,8 @@ struct RoomScreen_Previews: PreviewProvider, TestablePreview {
|
||||
roomProxy: RoomProxyMock(with: .init(displayName: "Preview room", isCallOngoing: true)),
|
||||
appSettings: ServiceLocator.shared.settings,
|
||||
analytics: ServiceLocator.shared.analytics,
|
||||
userIndicatorController: ServiceLocator.shared.userIndicatorController)
|
||||
userIndicatorController: ServiceLocator.shared.userIndicatorController,
|
||||
application: ApplicationMock.default)
|
||||
|
||||
static var previews: some View {
|
||||
NavigationStack {
|
||||
|
@ -65,7 +65,8 @@ struct TimelineReadReceiptsView_Previews: PreviewProvider, TestablePreview {
|
||||
members: members)),
|
||||
appSettings: ServiceLocator.shared.settings,
|
||||
analytics: ServiceLocator.shared.analytics,
|
||||
userIndicatorController: ServiceLocator.shared.userIndicatorController)
|
||||
userIndicatorController: ServiceLocator.shared.userIndicatorController,
|
||||
application: ApplicationMock.default)
|
||||
|
||||
static let singleReceipt = [ReadReceipt(userID: RoomMemberProxyMock.mockAlice.userID, formattedTimestamp: "Now")]
|
||||
static let doubleReceipt = [ReadReceipt(userID: RoomMemberProxyMock.mockAlice.userID, formattedTimestamp: "Now"),
|
||||
|
@ -86,7 +86,8 @@ struct UITimelineView_Previews: PreviewProvider, TestablePreview {
|
||||
roomProxy: RoomProxyMock(with: .init(displayName: "Preview room")),
|
||||
appSettings: ServiceLocator.shared.settings,
|
||||
analytics: ServiceLocator.shared.analytics,
|
||||
userIndicatorController: ServiceLocator.shared.userIndicatorController)
|
||||
userIndicatorController: ServiceLocator.shared.userIndicatorController,
|
||||
application: ApplicationMock.default)
|
||||
|
||||
static var previews: some View {
|
||||
NavigationStack {
|
||||
|
@ -174,7 +174,8 @@ struct TimelineView_Previews: PreviewProvider, TestablePreview {
|
||||
roomProxy: RoomProxyMock(with: .init(displayName: "Preview room")),
|
||||
appSettings: ServiceLocator.shared.settings,
|
||||
analytics: ServiceLocator.shared.analytics,
|
||||
userIndicatorController: ServiceLocator.shared.userIndicatorController)
|
||||
userIndicatorController: ServiceLocator.shared.userIndicatorController,
|
||||
application: ApplicationMock.default)
|
||||
|
||||
static var previews: some View {
|
||||
NavigationStack {
|
||||
|
@ -17,16 +17,23 @@
|
||||
import Foundation
|
||||
import UIKit
|
||||
|
||||
// sourcery: AutoMockable
|
||||
protocol ApplicationProtocol {
|
||||
func beginBackgroundTask(expirationHandler handler: (() -> Void)?) -> UIBackgroundTaskIdentifier
|
||||
|
||||
func beginBackgroundTask(withName taskName: String?, expirationHandler handler: (() -> Void)?) -> UIBackgroundTaskIdentifier
|
||||
|
||||
func endBackgroundTask(_ identifier: UIBackgroundTaskIdentifier)
|
||||
|
||||
func open(_ url: URL)
|
||||
|
||||
var backgroundTimeRemaining: TimeInterval { get }
|
||||
|
||||
var applicationState: UIApplication.State { get }
|
||||
}
|
||||
|
||||
extension UIApplication: ApplicationProtocol { }
|
||||
extension UIApplication: ApplicationProtocol {
|
||||
func open(_ url: URL) {
|
||||
open(url, options: [:], completionHandler: nil)
|
||||
}
|
||||
}
|
||||
|
@ -35,7 +35,7 @@ class BackgroundTaskTests: XCTestCase {
|
||||
|
||||
func testInitAndStop() {
|
||||
let service = UIKitBackgroundTaskService {
|
||||
UIApplication.mockHealty
|
||||
ApplicationMock.default
|
||||
}
|
||||
guard let task = service.startBackgroundTask(withName: Constants.bgTaskName) else {
|
||||
XCTFail("Failed to setup test conditions")
|
||||
@ -53,7 +53,7 @@ class BackgroundTaskTests: XCTestCase {
|
||||
|
||||
func testNotReusableInit() {
|
||||
let service = UIKitBackgroundTaskService {
|
||||
UIApplication.mockHealty
|
||||
ApplicationMock.default
|
||||
}
|
||||
|
||||
// create two not reusable task with the same name
|
||||
@ -70,7 +70,7 @@ class BackgroundTaskTests: XCTestCase {
|
||||
|
||||
func testReusableInit() {
|
||||
let service = UIKitBackgroundTaskService {
|
||||
UIApplication.mockHealty
|
||||
ApplicationMock.default
|
||||
}
|
||||
|
||||
// create two reusable task with the same name
|
||||
@ -91,7 +91,7 @@ class BackgroundTaskTests: XCTestCase {
|
||||
|
||||
func testMultipleStops() {
|
||||
let service = UIKitBackgroundTaskService {
|
||||
UIApplication.mockHealty
|
||||
ApplicationMock.default
|
||||
}
|
||||
|
||||
// create two reusable task with the same name
|
||||
@ -114,7 +114,7 @@ class BackgroundTaskTests: XCTestCase {
|
||||
|
||||
func testNotValidReuse() {
|
||||
let service = UIKitBackgroundTaskService {
|
||||
UIApplication.mockHealty
|
||||
ApplicationMock.default
|
||||
}
|
||||
|
||||
// create two reusable task with the same name
|
||||
@ -136,7 +136,7 @@ class BackgroundTaskTests: XCTestCase {
|
||||
|
||||
func testValidReuse() {
|
||||
let service = UIKitBackgroundTaskService {
|
||||
UIApplication.mockHealty
|
||||
ApplicationMock.default
|
||||
}
|
||||
|
||||
// create two reusable task with the same name
|
||||
@ -162,7 +162,7 @@ class BackgroundTaskTests: XCTestCase {
|
||||
|
||||
func testBrokenApp() {
|
||||
let service = UIKitBackgroundTaskService {
|
||||
UIApplication.mockBroken
|
||||
ApplicationMock.mockBroken
|
||||
}
|
||||
|
||||
// create two reusable task with the same name
|
||||
@ -173,7 +173,7 @@ class BackgroundTaskTests: XCTestCase {
|
||||
|
||||
func testNoTimeApp() {
|
||||
let service = UIKitBackgroundTaskService {
|
||||
UIApplication.mockAboutToSuspend
|
||||
ApplicationMock.mockAboutToSuspend
|
||||
}
|
||||
|
||||
// create two reusable task with the same name
|
||||
@ -182,67 +182,3 @@ class BackgroundTaskTests: XCTestCase {
|
||||
XCTAssertNil(task, "Task should not be created")
|
||||
}
|
||||
}
|
||||
|
||||
private extension UIApplication {
|
||||
static var mockHealty: ApplicationProtocol {
|
||||
MockApplication()
|
||||
}
|
||||
|
||||
static var mockBroken: ApplicationProtocol {
|
||||
MockApplication(withState: .inactive,
|
||||
backgroundTimeRemaining: 0,
|
||||
allowTasks: false)
|
||||
}
|
||||
|
||||
static var mockAboutToSuspend: ApplicationProtocol {
|
||||
MockApplication(withState: .background,
|
||||
backgroundTimeRemaining: 2,
|
||||
allowTasks: false)
|
||||
}
|
||||
}
|
||||
|
||||
private class MockApplication: ApplicationProtocol {
|
||||
let applicationState: UIApplication.State
|
||||
let backgroundTimeRemaining: TimeInterval
|
||||
private let allowTasks: Bool
|
||||
|
||||
init(withState applicationState: UIApplication.State = .active,
|
||||
backgroundTimeRemaining: TimeInterval = 10,
|
||||
allowTasks: Bool = true) {
|
||||
self.applicationState = applicationState
|
||||
self.backgroundTimeRemaining = backgroundTimeRemaining
|
||||
self.allowTasks = allowTasks
|
||||
}
|
||||
|
||||
private static var bgTaskIdentifier = 0
|
||||
|
||||
private var bgTasks: [UIBackgroundTaskIdentifier: Bool] = [:]
|
||||
|
||||
func beginBackgroundTask(expirationHandler handler: (() -> Void)?) -> UIBackgroundTaskIdentifier {
|
||||
guard allowTasks else {
|
||||
return .invalid
|
||||
}
|
||||
return beginBackgroundTask(withName: nil, expirationHandler: handler)
|
||||
}
|
||||
|
||||
func beginBackgroundTask(withName taskName: String?, expirationHandler handler: (() -> Void)?) -> UIBackgroundTaskIdentifier {
|
||||
guard allowTasks else {
|
||||
return .invalid
|
||||
}
|
||||
Self.bgTaskIdentifier += 1
|
||||
|
||||
let identifier = UIBackgroundTaskIdentifier(rawValue: Self.bgTaskIdentifier)
|
||||
bgTasks[identifier] = true
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
|
||||
handler?()
|
||||
}
|
||||
return identifier
|
||||
}
|
||||
|
||||
func endBackgroundTask(_ identifier: UIBackgroundTaskIdentifier) {
|
||||
guard allowTasks else {
|
||||
return
|
||||
}
|
||||
bgTasks.removeValue(forKey: identifier)
|
||||
}
|
||||
}
|
||||
|
@ -32,7 +32,8 @@ class PillContextTests: XCTestCase {
|
||||
roomProxy: proxyMock,
|
||||
appSettings: ServiceLocator.shared.settings,
|
||||
analytics: ServiceLocator.shared.analytics,
|
||||
userIndicatorController: ServiceLocator.shared.userIndicatorController)
|
||||
userIndicatorController: ServiceLocator.shared.userIndicatorController,
|
||||
application: ApplicationMock.default)
|
||||
let context = PillContext(roomContext: mock.context, data: PillTextAttachmentData(type: .user(userID: id), font: .preferredFont(forTextStyle: .body)))
|
||||
|
||||
XCTAssertFalse(context.viewState.isOwnMention)
|
||||
@ -64,7 +65,8 @@ class PillContextTests: XCTestCase {
|
||||
roomProxy: proxyMock,
|
||||
appSettings: ServiceLocator.shared.settings,
|
||||
analytics: ServiceLocator.shared.analytics,
|
||||
userIndicatorController: ServiceLocator.shared.userIndicatorController)
|
||||
userIndicatorController: ServiceLocator.shared.userIndicatorController,
|
||||
application: ApplicationMock.default)
|
||||
let context = PillContext(roomContext: mock.context, data: PillTextAttachmentData(type: .user(userID: id), font: .preferredFont(forTextStyle: .body)))
|
||||
|
||||
XCTAssertTrue(context.viewState.isOwnMention)
|
||||
@ -83,7 +85,8 @@ class PillContextTests: XCTestCase {
|
||||
roomProxy: proxyMock,
|
||||
appSettings: ServiceLocator.shared.settings,
|
||||
analytics: ServiceLocator.shared.analytics,
|
||||
userIndicatorController: ServiceLocator.shared.userIndicatorController)
|
||||
userIndicatorController: ServiceLocator.shared.userIndicatorController,
|
||||
application: ApplicationMock.default)
|
||||
let context = PillContext(roomContext: mock.context, data: PillTextAttachmentData(type: .allUsers, font: .preferredFont(forTextStyle: .body)))
|
||||
|
||||
XCTAssertTrue(context.viewState.isOwnMention)
|
||||
|
@ -55,7 +55,8 @@ class RoomScreenViewModelTests: XCTestCase {
|
||||
roomProxy: RoomProxyMock(with: .init(displayName: "")),
|
||||
appSettings: ServiceLocator.shared.settings,
|
||||
analytics: ServiceLocator.shared.analytics,
|
||||
userIndicatorController: userIndicatorControllerMock)
|
||||
userIndicatorController: userIndicatorControllerMock,
|
||||
application: ApplicationMock.default)
|
||||
|
||||
// Then the messages should be grouped together.
|
||||
XCTAssertEqual(viewModel.state.timelineViewState.itemViewStates[0].groupStyle, .first, "Nothing should prevent the first message from being grouped.")
|
||||
@ -89,7 +90,8 @@ class RoomScreenViewModelTests: XCTestCase {
|
||||
roomProxy: RoomProxyMock(with: .init(displayName: "")),
|
||||
appSettings: ServiceLocator.shared.settings,
|
||||
analytics: ServiceLocator.shared.analytics,
|
||||
userIndicatorController: userIndicatorControllerMock)
|
||||
userIndicatorController: userIndicatorControllerMock,
|
||||
application: ApplicationMock.default)
|
||||
|
||||
// Then the messages should be grouped by sender.
|
||||
XCTAssertEqual(viewModel.state.timelineViewState.itemViewStates[0].groupStyle, .single, "A message should not be grouped when the sender changes.")
|
||||
@ -121,7 +123,8 @@ class RoomScreenViewModelTests: XCTestCase {
|
||||
roomProxy: RoomProxyMock(with: .init(displayName: "")),
|
||||
appSettings: ServiceLocator.shared.settings,
|
||||
analytics: ServiceLocator.shared.analytics,
|
||||
userIndicatorController: userIndicatorControllerMock)
|
||||
userIndicatorController: userIndicatorControllerMock,
|
||||
application: ApplicationMock.default)
|
||||
|
||||
// Then the first message should not be grouped but the other two should.
|
||||
XCTAssertEqual(viewModel.state.timelineViewState.itemViewStates[0].groupStyle, .single, "When the first message has reactions it should not be grouped.")
|
||||
@ -150,7 +153,8 @@ class RoomScreenViewModelTests: XCTestCase {
|
||||
roomProxy: RoomProxyMock(with: .init(displayName: "")),
|
||||
appSettings: ServiceLocator.shared.settings,
|
||||
analytics: ServiceLocator.shared.analytics,
|
||||
userIndicatorController: userIndicatorControllerMock)
|
||||
userIndicatorController: userIndicatorControllerMock,
|
||||
application: ApplicationMock.default)
|
||||
|
||||
// Then the first and second messages should be grouped and the last one should not.
|
||||
XCTAssertEqual(viewModel.state.timelineViewState.itemViewStates[0].groupStyle, .first, "Nothing should prevent the first message from being grouped.")
|
||||
@ -179,7 +183,8 @@ class RoomScreenViewModelTests: XCTestCase {
|
||||
roomProxy: RoomProxyMock(with: .init(displayName: "")),
|
||||
appSettings: ServiceLocator.shared.settings,
|
||||
analytics: ServiceLocator.shared.analytics,
|
||||
userIndicatorController: userIndicatorControllerMock)
|
||||
userIndicatorController: userIndicatorControllerMock,
|
||||
application: ApplicationMock.default)
|
||||
|
||||
// Then the messages should be grouped together.
|
||||
XCTAssertEqual(viewModel.state.timelineViewState.itemViewStates[0].groupStyle, .first, "Nothing should prevent the first message from being grouped.")
|
||||
@ -204,7 +209,8 @@ class RoomScreenViewModelTests: XCTestCase {
|
||||
roomProxy: roomProxyMock,
|
||||
appSettings: ServiceLocator.shared.settings,
|
||||
analytics: ServiceLocator.shared.analytics,
|
||||
userIndicatorController: userIndicatorControllerMock)
|
||||
userIndicatorController: userIndicatorControllerMock,
|
||||
application: ApplicationMock.default)
|
||||
viewModel.actions
|
||||
.sink { action in
|
||||
switch action {
|
||||
@ -243,7 +249,8 @@ class RoomScreenViewModelTests: XCTestCase {
|
||||
roomProxy: roomProxyMock,
|
||||
appSettings: ServiceLocator.shared.settings,
|
||||
analytics: ServiceLocator.shared.analytics,
|
||||
userIndicatorController: userIndicatorControllerMock)
|
||||
userIndicatorController: userIndicatorControllerMock,
|
||||
application: ApplicationMock.default)
|
||||
|
||||
viewModel.actions
|
||||
.sink { action in
|
||||
@ -283,7 +290,8 @@ class RoomScreenViewModelTests: XCTestCase {
|
||||
roomProxy: roomProxyMock,
|
||||
appSettings: ServiceLocator.shared.settings,
|
||||
analytics: ServiceLocator.shared.analytics,
|
||||
userIndicatorController: userIndicatorControllerMock)
|
||||
userIndicatorController: userIndicatorControllerMock,
|
||||
application: ApplicationMock.default)
|
||||
viewModel.actions
|
||||
.sink { _ in
|
||||
XCTFail("Should not receive any action")
|
||||
@ -315,7 +323,8 @@ class RoomScreenViewModelTests: XCTestCase {
|
||||
roomProxy: roomProxyMock,
|
||||
appSettings: ServiceLocator.shared.settings,
|
||||
analytics: ServiceLocator.shared.analytics,
|
||||
userIndicatorController: userIndicatorControllerMock)
|
||||
userIndicatorController: userIndicatorControllerMock,
|
||||
application: ApplicationMock.default)
|
||||
|
||||
viewModel.context.send(viewAction: .retrySend(itemID: .init(timelineID: UUID().uuidString, transactionID: "test retry send id")))
|
||||
|
||||
@ -335,7 +344,8 @@ class RoomScreenViewModelTests: XCTestCase {
|
||||
roomProxy: roomProxyMock,
|
||||
appSettings: ServiceLocator.shared.settings,
|
||||
analytics: ServiceLocator.shared.analytics,
|
||||
userIndicatorController: userIndicatorControllerMock)
|
||||
userIndicatorController: userIndicatorControllerMock,
|
||||
application: ApplicationMock.default)
|
||||
|
||||
viewModel.context.send(viewAction: .retrySend(itemID: .random))
|
||||
|
||||
@ -354,7 +364,8 @@ class RoomScreenViewModelTests: XCTestCase {
|
||||
roomProxy: roomProxyMock,
|
||||
appSettings: ServiceLocator.shared.settings,
|
||||
analytics: ServiceLocator.shared.analytics,
|
||||
userIndicatorController: userIndicatorControllerMock)
|
||||
userIndicatorController: userIndicatorControllerMock,
|
||||
application: ApplicationMock.default)
|
||||
|
||||
viewModel.context.send(viewAction: .cancelSend(itemID: .init(timelineID: UUID().uuidString, transactionID: "test cancel send id")))
|
||||
|
||||
@ -374,7 +385,8 @@ class RoomScreenViewModelTests: XCTestCase {
|
||||
roomProxy: roomProxyMock,
|
||||
appSettings: ServiceLocator.shared.settings,
|
||||
analytics: ServiceLocator.shared.analytics,
|
||||
userIndicatorController: userIndicatorControllerMock)
|
||||
userIndicatorController: userIndicatorControllerMock,
|
||||
application: ApplicationMock.default)
|
||||
|
||||
viewModel.context.send(viewAction: .cancelSend(itemID: .random))
|
||||
|
||||
@ -512,6 +524,7 @@ class RoomScreenViewModelTests: XCTestCase {
|
||||
appSettings: ServiceLocator.shared.settings,
|
||||
analytics: ServiceLocator.shared.analytics,
|
||||
userIndicatorController: userIndicatorControllerMock,
|
||||
application: ApplicationMock.default,
|
||||
notificationCenterProtocol: notificationCenter)
|
||||
|
||||
return (viewModel, roomProxy, timelineController, notificationCenter)
|
||||
|
Loading…
x
Reference in New Issue
Block a user