diff --git a/ElementX.xcodeproj/project.pbxproj b/ElementX.xcodeproj/project.pbxproj index dbc8b7d8d..bf588dd75 100644 --- a/ElementX.xcodeproj/project.pbxproj +++ b/ElementX.xcodeproj/project.pbxproj @@ -208,6 +208,7 @@ 6FC10A00D268FCD48B631E37 /* ViewFrameReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = EFF7BF82A950B91BC5469E91 /* ViewFrameReader.swift */; }; 7002C55A4C917F3715765127 /* MediaProviderProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = C888BCD78E2A55DCE364F160 /* MediaProviderProtocol.swift */; }; 702694459B649B9D3A3C34F8 /* TimelineTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9212AE02CBDD692C56A879F /* TimelineTableViewController.swift */; }; + 70558528EF68CAAEF09972D5 /* RoomTimelineItemFixtures.swift in Sources */ = {isa = PBXBuildFile; fileRef = E96ED747FF90332EA1333C22 /* RoomTimelineItemFixtures.swift */; }; 706F79A39BDB32F592B8C2C7 /* UIKitBackgroundTask.swift in Sources */ = {isa = PBXBuildFile; fileRef = 92FCD9116ADDE820E4E30F92 /* UIKitBackgroundTask.swift */; }; 7096FA3AC218D914E88BFB70 /* AggregratedReaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = F15BE37BE2FB86E00C8D150A /* AggregratedReaction.swift */; }; 719E7AAD1F8E68F68F30FECD /* Task.swift in Sources */ = {isa = PBXBuildFile; fileRef = A40C19719687984FD9478FBE /* Task.swift */; }; @@ -964,6 +965,7 @@ E6281B199D8A8F0892490C2E /* OnboardingCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingCoordinator.swift; sourceTree = ""; }; E8294DB9E95C0C0630418466 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/Localizable.strings; sourceTree = ""; }; E8CA187FE656EE5A3F6C7DE5 /* UIFont+AttributedStringBuilder.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "UIFont+AttributedStringBuilder.m"; sourceTree = ""; }; + E96ED747FF90332EA1333C22 /* RoomTimelineItemFixtures.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomTimelineItemFixtures.swift; sourceTree = ""; }; E9D059BFE329BE09B6D96A9F /* ro */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = ro; path = ro.lproj/Localizable.stringsdict; sourceTree = ""; }; EB3B237387B8288A5A938F1B /* UserAgentBuilderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserAgentBuilderTests.swift; sourceTree = ""; }; EBE5502760CF6CA2D7201883 /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = ja; path = ja.lproj/Localizable.stringsdict; sourceTree = ""; }; @@ -1295,6 +1297,14 @@ path = Helpers; sourceTree = ""; }; + 3EA31CC7012EA2A5653DAFC9 /* Fixtures */ = { + isa = PBXGroup; + children = ( + E96ED747FF90332EA1333C22 /* RoomTimelineItemFixtures.swift */, + ); + path = Fixtures; + sourceTree = ""; + }; 3F38EAC92E2281990E65DAF2 /* OnboardingScreen */ = { isa = PBXGroup; children = ( @@ -2293,6 +2303,7 @@ 66F2402D738694F98729A441 /* RoomTimelineProvider.swift */, 095AED4CF56DFF3EB7BB84C8 /* RoomTimelineProviderProtocol.swift */, 2D505843AB66822EB91F0DF0 /* TimelineItemProxy.swift */, + 3EA31CC7012EA2A5653DAFC9 /* Fixtures */, 5A7A7D6D373D411C8C48B881 /* TimeLineItemContent */, 95BE1C7CB2C80344FF0BE724 /* TimelineItems */, ); @@ -2973,6 +2984,7 @@ 9B8DE1D424E37581C7D99CCC /* RoomTimelineControllerProtocol.swift in Sources */, 4E945AD6862C403F74E57755 /* RoomTimelineItemFactory.swift in Sources */, 13C77FDF17C4C6627CFFC205 /* RoomTimelineItemFactoryProtocol.swift in Sources */, + 70558528EF68CAAEF09972D5 /* RoomTimelineItemFixtures.swift in Sources */, C8E82786DE1B6A400DA9BA25 /* RoomTimelineItemProperties.swift in Sources */, 1AE4AEA0FA8DEF52671832E0 /* RoomTimelineItemProtocol.swift in Sources */, 9BD3A773186291560DF92B62 /* RoomTimelineProvider.swift in Sources */, diff --git a/ElementX/Sources/Screens/RoomScreen/View/TimelineView.swift b/ElementX/Sources/Screens/RoomScreen/View/TimelineView.swift index 7ddaae723..fdb56c140 100644 --- a/ElementX/Sources/Screens/RoomScreen/View/TimelineView.swift +++ b/ElementX/Sources/Screens/RoomScreen/View/TimelineView.swift @@ -26,7 +26,6 @@ struct TimelineView: UIViewControllerRepresentable { timelineStyle: timelineStyle, scrollToBottomButtonVisible: $viewModelContext.scrollToBottomButtonVisible, scrollToBottomPublisher: viewModelContext.viewState.scrollToBottomPublisher) - viewModelContext.send(viewAction: .paginateBackwards) return tableViewController } @@ -46,6 +45,10 @@ struct TimelineView: UIViewControllerRepresentable { init(viewModelContext: RoomScreenViewModel.Context) { context = viewModelContext + + if viewModelContext.viewState.items.isEmpty { + viewModelContext.send(viewAction: .paginateBackwards) + } } /// Updates the specified table view's properties from the current view state. diff --git a/ElementX/Sources/Services/Timeline/Fixtures/RoomTimelineItemFixtures.swift b/ElementX/Sources/Services/Timeline/Fixtures/RoomTimelineItemFixtures.swift new file mode 100644 index 000000000..bf17b1457 --- /dev/null +++ b/ElementX/Sources/Services/Timeline/Fixtures/RoomTimelineItemFixtures.swift @@ -0,0 +1,222 @@ +// +// Copyright 2022 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 Foundation + +enum RoomTimelineItemFixtures { + /// The default timeline items used in Xcode previews etc. + static var `default`: [RoomTimelineItemProtocol] = [ + SeparatorRoomTimelineItem(id: UUID().uuidString, + text: "Yesterday"), + TextRoomTimelineItem(id: UUID().uuidString, + text: "That looks so good!", + timestamp: "10:10 AM", + inGroupState: .single, + isOutgoing: false, + isEditable: false, + senderId: "", + senderDisplayName: "Jacob", + properties: RoomTimelineItemProperties(isEdited: true)), + TextRoomTimelineItem(id: UUID().uuidString, + text: "Letโ€™s get lunch soon! New salad place opened up ๐Ÿฅ—. When are yโ€™all free? ๐Ÿค—", + timestamp: "10:11 AM", + inGroupState: .beginning, + isOutgoing: false, + isEditable: false, + senderId: "", + senderDisplayName: "Helena", + properties: RoomTimelineItemProperties(reactions: [ + AggregatedReaction(key: "๐Ÿ™Œ", count: 1, isHighlighted: true) + ])), + TextRoomTimelineItem(id: UUID().uuidString, + text: "I can be around on Wednesday. How about some ๐ŸŒฎ instead? Like https://www.tortilla.co.uk/", + timestamp: "10:11 AM", + inGroupState: .end, + isOutgoing: false, + isEditable: false, + senderId: "", + senderDisplayName: "Helena", + properties: RoomTimelineItemProperties(reactions: [ + AggregatedReaction(key: "๐Ÿ™", count: 1, isHighlighted: false), + AggregatedReaction(key: "๐Ÿ™Œ", count: 2, isHighlighted: true) + ])), + SeparatorRoomTimelineItem(id: UUID().uuidString, + text: "Today"), + TextRoomTimelineItem(id: UUID().uuidString, + text: "Wow, cool. Ok, lets go the usual place tomorrow?! Is that too soon? Hereโ€™s the menu, let me know what you want itโ€™s on me!", + timestamp: "5 PM", + inGroupState: .single, + isOutgoing: false, + isEditable: false, + senderId: "", + senderDisplayName: "Helena"), + TextRoomTimelineItem(id: UUID().uuidString, + text: "And John's speech was amazing!", + timestamp: "5 PM", + inGroupState: .beginning, + isOutgoing: true, + isEditable: true, + senderId: "", + senderDisplayName: "Bob"), + TextRoomTimelineItem(id: UUID().uuidString, + text: "New home office set up!", + timestamp: "5 PM", + inGroupState: .end, + isOutgoing: true, + isEditable: true, + senderId: "", + senderDisplayName: "Bob", + properties: RoomTimelineItemProperties(reactions: [ + AggregatedReaction(key: "๐Ÿ™", count: 1, isHighlighted: false), + AggregatedReaction(key: "๐Ÿ˜", count: 3, isHighlighted: false) + ])), + TextRoomTimelineItem(id: UUID().uuidString, + text: "", + attributedComponents: [ + AttributedStringBuilderComponent(attributedString: "Hol' up", isBlockquote: false), + AttributedStringBuilderComponent(attributedString: "New home office set up!", isBlockquote: true), + AttributedStringBuilderComponent(attributedString: "That's amazing! Congrats ๐Ÿฅณ", isBlockquote: false) + ], + timestamp: "5 PM", + inGroupState: .single, + isOutgoing: false, + isEditable: false, + senderId: "", + senderDisplayName: "Helena") + ] + + /// A small chunk of events, containing 2 text items. + static var smallChunk: [RoomTimelineItemProtocol] { + [TextRoomTimelineItem(text: "Hey there ๐Ÿ‘‹", + inGroupState: .beginning, + senderDisplayName: "Alice"), + TextRoomTimelineItem(text: "How are you?", + inGroupState: .end, + senderDisplayName: "Alice")] + } + + /// A chunk of events that contains a single text item. + static var singleMessageChunk: [RoomTimelineItemProtocol] { + [TextRoomTimelineItem(text: "Tap tap tap ๐ŸŽ™๏ธ. Is this thing on?", + inGroupState: .single, + senderDisplayName: "Helena")] + } + + /// A single text item. + static var incomingMessage: RoomTimelineItemProtocol { + TextRoomTimelineItem(text: "Hello, World!", + inGroupState: .single, + senderDisplayName: "Bob") + } + + /// A large chunk of events, containing 40 text items which should fill an iPad + /// with enough items so that it won't perform another back pagination. + static var largeChunk: [RoomTimelineItemProtocol] { + [TextRoomTimelineItem(text: "Bacon ipsum dolor amet commodo incididunt ribeye dolore cupidatat short ribs.", + senderDisplayName: "Bob"), + TextRoomTimelineItem(text: "Labore ipsum jowl meatloaf adipisicing ham leberkas.", + senderDisplayName: "Alice"), + TextRoomTimelineItem(text: "Tongue culpa dolor, short ribs doner cillum do rump id nulla mollit.", + senderDisplayName: "Helena"), + TextRoomTimelineItem(text: "Capicola laborum aute porchetta, kevin ut ut bacon swine kielbasa beef rump ipsum.", + senderDisplayName: "Alice"), + TextRoomTimelineItem(text: "Leberkas beef ad salami flank laborum ex veniam excepteur picanha occaecat burgdoggen.", + senderDisplayName: "Bob"), + TextRoomTimelineItem(text: "Magna leberkas nostrud laboris, biltong in tongue nulla et id drumstick brisket.", + senderDisplayName: "Helena"), + TextRoomTimelineItem(text: "Landjaeger adipisicing spare ribs sunt pig voluptate beef ribs venison ut meatloaf nulla beef sed.", + senderDisplayName: "Bob"), + TextRoomTimelineItem(text: "Bacon chicken excepteur, filet mignon pastrami meatball ribeye sunt sausage.", + senderDisplayName: "Alice"), + TextRoomTimelineItem(text: "Ham et dolore, nisi adipisicing kielbasa andouille ribeye enim chicken.", + senderDisplayName: "Helena"), + TextRoomTimelineItem(text: "Ribeye prosciutto aliquip tail dolore.", + senderDisplayName: "Alice"), + TextRoomTimelineItem(text: "Salami culpa exercitation ea non rump consectetur ipsum boudin irure jerky spare ribs duis leberkas pastrami.", + senderDisplayName: "Bob"), + TextRoomTimelineItem(text: "Andouille shankle magna pig corned beef strip steak ex landjaeger sed chicken drumstick.", + senderDisplayName: "Helena"), + TextRoomTimelineItem(text: "Deserunt ea esse quis bresaola, ham hock sirloin spare ribs porchetta dolore ham nisi est.", + senderDisplayName: "Bob"), + TextRoomTimelineItem(text: "Consectetur nulla laboris, rump minim tempor turducken sunt tongue in.", + senderDisplayName: "Alice"), + TextRoomTimelineItem(text: "Ea ut laboris eu spare ribs occaecat esse et shankle chicken.", + senderDisplayName: "Helena"), + TextRoomTimelineItem(text: "Frankfurter brisket eu, landjaeger ea ham hamburger rump eiusmod pastrami cow.", + senderDisplayName: "Alice"), + TextRoomTimelineItem(text: "Qui prosciutto sed, officia occaecat drumstick non veniam in elit chicken capicola buffalo beef ribs irure.", + senderDisplayName: "Bob"), + TextRoomTimelineItem(text: "In pork loin lorem, pariatur tail cupim voluptate chicken id eu pancetta esse pastrami.", + senderDisplayName: "Helena"), + TextRoomTimelineItem(text: "Excepteur minim ea est, jerky sirloin frankfurter nisi dolor ball tip.", + senderDisplayName: "Bob"), + TextRoomTimelineItem(text: "Shank corned beef velit chislic, pork chop enim in chuck in excepteur fatback minim.", + senderDisplayName: "Alice"), + TextRoomTimelineItem(text: "Mollit minim ipsum in, in do doner ribeye cow jowl short loin sed.", + senderDisplayName: "Helena"), + TextRoomTimelineItem(text: "Meatloaf est hamburger, spare ribs pork belly officia dolor.", + senderDisplayName: "Alice"), + TextRoomTimelineItem(text: "Pancetta do aliqua picanha tempor.", + senderDisplayName: "Bob"), + TextRoomTimelineItem(text: "Ad pig incididunt doner pork chop flank velit capicola aliqua.", + senderDisplayName: "Helena"), + TextRoomTimelineItem(text: "Ullamco ex qui kevin meatball, leberkas hamburger venison.", + senderDisplayName: "Bob"), + TextRoomTimelineItem(text: "Capicola et esse, fatback porchetta filet mignon ham nulla salami shank.", + senderDisplayName: "Alice"), + TextRoomTimelineItem(text: "Boudin adipisicing pancetta chuck spare ribs beef ribs, in ut pork kevin.", + senderDisplayName: "Helena"), + TextRoomTimelineItem(text: "Adipisicing pig short loin hamburger nisi exercitation landjaeger pancetta picanha ex cupim beef ribs.", + senderDisplayName: "Alice"), + TextRoomTimelineItem(text: "Burgdoggen tri-tip eu elit consectetur, hamburger dolore commodo bacon capicola esse ex exercitation anim nostrud.", + senderDisplayName: "Bob"), + TextRoomTimelineItem(text: "Id burgdoggen bresaola pork.", + senderDisplayName: "Helena"), + TextRoomTimelineItem(text: "Pariatur meatloaf dolore tenderloin ea et proident strip steak velit nostrud pork loin laboris.", + senderDisplayName: "Bob"), + TextRoomTimelineItem(text: "Pork chop cupim pastrami, prosciutto chislic kevin tempor eu ut deserunt ut occaecat consectetur non.", + senderDisplayName: "Alice"), + TextRoomTimelineItem(text: "Aliquip kevin fugiat esse, adipisicing bresaola andouille biltong.", + senderDisplayName: "Helena"), + TextRoomTimelineItem(text: "Andouille est picanha, beef ribs boudin exercitation flank venison ea tongue landjaeger meatloaf velit.", + senderDisplayName: "Alice"), + TextRoomTimelineItem(text: "Boudin rump hamburger laborum adipisicing consectetur officia frankfurter shoulder quis biltong fugiat esse.", + senderDisplayName: "Bob"), + TextRoomTimelineItem(text: "Ham hock culpa corned beef cupim pastrami swine in.", + senderDisplayName: "Helena"), + TextRoomTimelineItem(text: "Boudin adipisicing pancetta chuck spare ribs beef ribs, in ut pork kevin.", + senderDisplayName: "Bob"), + TextRoomTimelineItem(text: "Aliquip meatball incididunt fatback, pork belly in jowl tri-tip commodo spare ribs.", + senderDisplayName: "Alice"), + TextRoomTimelineItem(text: "Excepteur rump tri-tip culpa in shankle esse ut.", + senderDisplayName: "Helena"), + TextRoomTimelineItem(text: "Pork buffalo mollit culpa strip steak in leberkas flank cow.", + senderDisplayName: "Alice")] + } +} + +private extension TextRoomTimelineItem { + init(text: String, inGroupState: TimelineItemInGroupState = .single, senderDisplayName: String) { + self.init(id: UUID().uuidString, + text: text, + timestamp: "10:47 am", + inGroupState: inGroupState, + isOutgoing: senderDisplayName == "Alice", + isEditable: false, + senderId: "", + senderDisplayName: senderDisplayName) + } +} diff --git a/ElementX/Sources/Services/Timeline/MockRoomTimelineController.swift b/ElementX/Sources/Services/Timeline/MockRoomTimelineController.swift index 6ff81d646..96610d718 100644 --- a/ElementX/Sources/Services/Timeline/MockRoomTimelineController.swift +++ b/ElementX/Sources/Services/Timeline/MockRoomTimelineController.swift @@ -18,92 +18,54 @@ import Combine import Foundation class MockRoomTimelineController: RoomTimelineControllerProtocol { + /// An array of timeline item arrays that will be inserted in order for each back pagination request. + var backPaginationResponses: [[RoomTimelineItemProtocol]] = [] + /// The time delay added to each back pagination request. + var backPaginationDelay: Duration = .milliseconds(500) + + /// An array of timeline items that will be appended in order when ``simulateIncomingItems()`` is called. + var incomingItems: [RoomTimelineItemProtocol] = [] + /// The time delay between each incoming item. + var incomingDelay: Duration = .milliseconds(750) + let roomId = "MockRoomIdentifier" let callbacks = PassthroughSubject() - var timelineItems: [RoomTimelineItemProtocol] = [ - SeparatorRoomTimelineItem(id: UUID().uuidString, - text: "Yesterday"), - TextRoomTimelineItem(id: UUID().uuidString, - text: "That looks so good!", - timestamp: "10:10 AM", - inGroupState: .single, - isOutgoing: false, - isEditable: false, - senderId: "", - senderDisplayName: "Jacob", - properties: RoomTimelineItemProperties(isEdited: true)), - TextRoomTimelineItem(id: UUID().uuidString, - text: "Letโ€™s get lunch soon! New salad place opened up ๐Ÿฅ—. When are yโ€™all free? ๐Ÿค—", - timestamp: "10:11 AM", - inGroupState: .beginning, - isOutgoing: false, - isEditable: false, - senderId: "", - senderDisplayName: "Helena", - properties: RoomTimelineItemProperties(reactions: [ - AggregatedReaction(key: "๐Ÿ™Œ", count: 1, isHighlighted: true) - ])), - TextRoomTimelineItem(id: UUID().uuidString, - text: "I can be around on Wednesday. How about some ๐ŸŒฎ instead? Like https://www.tortilla.co.uk/", - timestamp: "10:11 AM", - inGroupState: .end, - isOutgoing: false, - isEditable: false, - senderId: "", - senderDisplayName: "Helena", - properties: RoomTimelineItemProperties(reactions: [ - AggregatedReaction(key: "๐Ÿ™", count: 1, isHighlighted: false), - AggregatedReaction(key: "๐Ÿ™Œ", count: 2, isHighlighted: true) - ])), - SeparatorRoomTimelineItem(id: UUID().uuidString, - text: "Today"), - TextRoomTimelineItem(id: UUID().uuidString, - text: "Wow, cool. Ok, lets go the usual place tomorrow?! Is that too soon? Hereโ€™s the menu, let me know what you want itโ€™s on me!", - timestamp: "5 PM", - inGroupState: .single, - isOutgoing: false, - isEditable: false, - senderId: "", - senderDisplayName: "Helena"), - TextRoomTimelineItem(id: UUID().uuidString, - text: "And John's speech was amazing!", - timestamp: "5 PM", - inGroupState: .beginning, - isOutgoing: true, - isEditable: true, - senderId: "", - senderDisplayName: "Bob"), - TextRoomTimelineItem(id: UUID().uuidString, - text: "New home office set up!", - timestamp: "5 PM", - inGroupState: .end, - isOutgoing: true, - isEditable: true, - senderId: "", - senderDisplayName: "Bob", - properties: RoomTimelineItemProperties(reactions: [ - AggregatedReaction(key: "๐Ÿ™", count: 1, isHighlighted: false), - AggregatedReaction(key: "๐Ÿ˜", count: 3, isHighlighted: false) - ])), - TextRoomTimelineItem(id: UUID().uuidString, - text: "", - attributedComponents: [ - AttributedStringBuilderComponent(attributedString: "Hol' up", isBlockquote: false), - AttributedStringBuilderComponent(attributedString: "New home office set up!", isBlockquote: true), - AttributedStringBuilderComponent(attributedString: "That's amazing! Congrats ๐Ÿฅณ", isBlockquote: false) - ], - timestamp: "5 PM", - inGroupState: .single, - isOutgoing: false, - isEditable: false, - senderId: "", - senderDisplayName: "Helena") - ] + var timelineItems: [RoomTimelineItemProtocol] = RoomTimelineItemFixtures.default + + func simulateIncomingItems() { + guard !incomingItems.isEmpty else { return } + + let incomingItem = incomingItems.removeFirst() + + Task { + try await Task.sleep(for: incomingDelay) + timelineItems.append(incomingItem) + callbacks.send(.updatedTimelineItems) + + if !self.incomingItems.isEmpty { + simulateIncomingItems() + } + } + } func paginateBackwards(_ count: UInt) async -> Result { - .failure(.generic) + callbacks.send(.startedBackPaginating) + + guard !backPaginationResponses.isEmpty else { + callbacks.send(.finishedBackPaginating) + return .failure(.generic) + } + + let newItems = backPaginationResponses.removeFirst() + + try? await Task.sleep(for: backPaginationDelay) + timelineItems.insert(contentsOf: newItems, at: 0) + callbacks.send(.updatedTimelineItems) + callbacks.send(.finishedBackPaginating) + + return .success(()) } func processItemAppearance(_ itemId: String) async { } diff --git a/ElementX/Sources/UITests/UITestScreenIdentifier.swift b/ElementX/Sources/UITests/UITestScreenIdentifier.swift index 77bc43a7d..1d64a19ff 100644 --- a/ElementX/Sources/UITests/UITestScreenIdentifier.swift +++ b/ElementX/Sources/UITests/UITestScreenIdentifier.swift @@ -32,6 +32,9 @@ enum UITestScreenIdentifier: String { case onboarding case roomPlainNoAvatar case roomEncryptedWithAvatar + case roomSmallTimeline + case roomSmallTimelineIncomingAndSmallPagination + case roomSmallTimelineLargePagination case sessionVerification } diff --git a/ElementX/Sources/UITests/UITestsAppCoordinator.swift b/ElementX/Sources/UITests/UITestsAppCoordinator.swift index 1b7b2ff3d..cd44e7ae4 100644 --- a/ElementX/Sources/UITests/UITestsAppCoordinator.swift +++ b/ElementX/Sources/UITests/UITestsAppCoordinator.swift @@ -23,6 +23,7 @@ class UITestsAppCoordinator: AppCoordinatorProtocol { let notificationManager: NotificationManagerProtocol? = nil init() { + UIView.setAnimationsEnabled(false) navigationStackCoordinator = NavigationStackCoordinator() ServiceLocator.shared.register(userNotificationController: MockUserNotificationController()) @@ -132,6 +133,40 @@ class MockScreen: Identifiable { roomAvatarUrl: "mock_url", emojiProvider: EmojiProvider()) return RoomScreenCoordinator(parameters: parameters) + case .roomSmallTimeline: + let timelineController = MockRoomTimelineController() + timelineController.timelineItems = RoomTimelineItemFixtures.smallChunk + let parameters = RoomScreenCoordinatorParameters(navigationStackCoordinator: navigationStackCoordinator, + timelineController: timelineController, + mediaProvider: MockMediaProvider(), + roomName: "New room", + roomAvatarUrl: "mock_url", + emojiProvider: EmojiProvider()) + return RoomScreenCoordinator(parameters: parameters) + case .roomSmallTimelineIncomingAndSmallPagination: + let timelineController = MockRoomTimelineController() + timelineController.timelineItems = RoomTimelineItemFixtures.smallChunk + timelineController.backPaginationResponses = [RoomTimelineItemFixtures.singleMessageChunk] + timelineController.incomingItems = [RoomTimelineItemFixtures.incomingMessage] + timelineController.simulateIncomingItems() + let parameters = RoomScreenCoordinatorParameters(navigationStackCoordinator: navigationStackCoordinator, + timelineController: timelineController, + mediaProvider: MockMediaProvider(), + roomName: "Small timeline", + roomAvatarUrl: "mock_url", + emojiProvider: EmojiProvider()) + return RoomScreenCoordinator(parameters: parameters) + case .roomSmallTimelineLargePagination: + let timelineController = MockRoomTimelineController() + timelineController.timelineItems = RoomTimelineItemFixtures.smallChunk + timelineController.backPaginationResponses = [RoomTimelineItemFixtures.largeChunk] + let parameters = RoomScreenCoordinatorParameters(navigationStackCoordinator: navigationStackCoordinator, + timelineController: timelineController, + mediaProvider: MockMediaProvider(), + roomName: "Small timeline, paginating", + roomAvatarUrl: "mock_url", + emojiProvider: EmojiProvider()) + return RoomScreenCoordinator(parameters: parameters) case .sessionVerification: let parameters = SessionVerificationCoordinatorParameters(sessionVerificationControllerProxy: MockSessionVerificationControllerProxy()) return SessionVerificationCoordinator(parameters: parameters) diff --git a/UITests/Sources/Application.swift b/UITests/Sources/Application.swift index 570717e19..d3827df2d 100644 --- a/UITests/Sources/Application.swift +++ b/UITests/Sources/Application.swift @@ -33,7 +33,7 @@ extension XCUIApplication { let lastLabel = staticTexts["lastItem"] while !button.isHittable, !lastLabel.isHittable { - tables.firstMatch.swipeUp() + swipeUp() } button.tap() diff --git a/UITests/Sources/RoomScreenUITests.swift b/UITests/Sources/RoomScreenUITests.swift index 1d77ae903..6dc18f26b 100644 --- a/UITests/Sources/RoomScreenUITests.swift +++ b/UITests/Sources/RoomScreenUITests.swift @@ -38,4 +38,28 @@ class RoomScreenUITests: XCTestCase { app.assertScreenshot(.roomEncryptedWithAvatar) } + + func testSmallTimelineLayout() { + let app = Application.launch() + app.goToScreenWithIdentifier(.roomSmallTimeline) + + // The messages should be bottom aligned. + app.assertScreenshot(.roomSmallTimeline) + } + + func testSmallTimelineWithIncomingAndPagination() { + let app = Application.launch() + app.goToScreenWithIdentifier(.roomSmallTimelineIncomingAndSmallPagination) + + // Wait for both the incoming message and the pagination chunk. + XCTAssert(app.staticTexts["Bob"].waitForExistence(timeout: 2)) + XCTAssert(app.staticTexts["Helena"].waitForExistence(timeout: 2)) + + // The messages should still be bottom aligned after the new items are added. + app.assertScreenshot(.roomSmallTimelineIncomingAndSmallPagination) + } + + func testSmallTimelineWithLargePagination() { + // To be implemented + } } diff --git a/UITests/Sources/__Snapshots__/Application/de-DE-iPad-9th-generation.roomSmallTimeline.png b/UITests/Sources/__Snapshots__/Application/de-DE-iPad-9th-generation.roomSmallTimeline.png new file mode 100644 index 000000000..3839a4657 --- /dev/null +++ b/UITests/Sources/__Snapshots__/Application/de-DE-iPad-9th-generation.roomSmallTimeline.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:96d580e92e1618c729240a00439801feccbfadf6bd7e00119a4df5b3724e1c82 +size 92567 diff --git a/UITests/Sources/__Snapshots__/Application/de-DE-iPad-9th-generation.roomSmallTimelineIncomingAndSmallPagination.png b/UITests/Sources/__Snapshots__/Application/de-DE-iPad-9th-generation.roomSmallTimelineIncomingAndSmallPagination.png new file mode 100644 index 000000000..ddd6e197e --- /dev/null +++ b/UITests/Sources/__Snapshots__/Application/de-DE-iPad-9th-generation.roomSmallTimelineIncomingAndSmallPagination.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:925c9edf55718ec2e1195fbe8a6e313f435eee075c703c74a487ca0425a60f2f +size 133380 diff --git a/UITests/Sources/__Snapshots__/Application/de-DE-iPhone-14.roomSmallTimeline.png b/UITests/Sources/__Snapshots__/Application/de-DE-iPhone-14.roomSmallTimeline.png new file mode 100644 index 000000000..cb19786c4 --- /dev/null +++ b/UITests/Sources/__Snapshots__/Application/de-DE-iPhone-14.roomSmallTimeline.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3295b6e8348a37998c95f72285ae0bfc5651ec3166a376c327f482a0baa1e93d +size 114678 diff --git a/UITests/Sources/__Snapshots__/Application/de-DE-iPhone-14.roomSmallTimelineIncomingAndSmallPagination.png b/UITests/Sources/__Snapshots__/Application/de-DE-iPhone-14.roomSmallTimelineIncomingAndSmallPagination.png new file mode 100644 index 000000000..147fdf005 --- /dev/null +++ b/UITests/Sources/__Snapshots__/Application/de-DE-iPhone-14.roomSmallTimelineIncomingAndSmallPagination.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:015eb7e93e193d635194bb6d0d67a3a8751180598fc63762ada0f94be11099e9 +size 212903 diff --git a/UITests/Sources/__Snapshots__/Application/en-GB-iPad-9th-generation.roomSmallTimeline.png b/UITests/Sources/__Snapshots__/Application/en-GB-iPad-9th-generation.roomSmallTimeline.png new file mode 100644 index 000000000..ca43bee6b --- /dev/null +++ b/UITests/Sources/__Snapshots__/Application/en-GB-iPad-9th-generation.roomSmallTimeline.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:12af6767660c17c4eaebdb951da4e61ac1cc365ba950e237fe8f08d4707f39a5 +size 92342 diff --git a/UITests/Sources/__Snapshots__/Application/en-GB-iPad-9th-generation.roomSmallTimelineIncomingAndSmallPagination.png b/UITests/Sources/__Snapshots__/Application/en-GB-iPad-9th-generation.roomSmallTimelineIncomingAndSmallPagination.png new file mode 100644 index 000000000..25ce993a4 --- /dev/null +++ b/UITests/Sources/__Snapshots__/Application/en-GB-iPad-9th-generation.roomSmallTimelineIncomingAndSmallPagination.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:087309a5c640fe574e3f80e9e8cbccef71bced0d49016a27c963d7bff07e24d1 +size 133159 diff --git a/UITests/Sources/__Snapshots__/Application/en-GB-iPhone-14.roomSmallTimeline.png b/UITests/Sources/__Snapshots__/Application/en-GB-iPhone-14.roomSmallTimeline.png new file mode 100644 index 000000000..3fe712be4 --- /dev/null +++ b/UITests/Sources/__Snapshots__/Application/en-GB-iPhone-14.roomSmallTimeline.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a365ef80c8bce284dfddd840433afe9fa6b3d7eee8521da98e065777a6819530 +size 114689 diff --git a/UITests/Sources/__Snapshots__/Application/en-GB-iPhone-14.roomSmallTimelineIncomingAndSmallPagination.png b/UITests/Sources/__Snapshots__/Application/en-GB-iPhone-14.roomSmallTimelineIncomingAndSmallPagination.png new file mode 100644 index 000000000..9475fb9fc --- /dev/null +++ b/UITests/Sources/__Snapshots__/Application/en-GB-iPhone-14.roomSmallTimelineIncomingAndSmallPagination.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:31267576b8c4c8d038b3f5dde9e3f6c1f06c00b72cea3e5e9d02765e231fa682 +size 212873 diff --git a/UITests/Sources/__Snapshots__/Application/fr-FR-iPad-9th-generation.roomSmallTimeline.png b/UITests/Sources/__Snapshots__/Application/fr-FR-iPad-9th-generation.roomSmallTimeline.png new file mode 100644 index 000000000..ca43bee6b --- /dev/null +++ b/UITests/Sources/__Snapshots__/Application/fr-FR-iPad-9th-generation.roomSmallTimeline.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:12af6767660c17c4eaebdb951da4e61ac1cc365ba950e237fe8f08d4707f39a5 +size 92342 diff --git a/UITests/Sources/__Snapshots__/Application/fr-FR-iPad-9th-generation.roomSmallTimelineIncomingAndSmallPagination.png b/UITests/Sources/__Snapshots__/Application/fr-FR-iPad-9th-generation.roomSmallTimelineIncomingAndSmallPagination.png new file mode 100644 index 000000000..25ce993a4 --- /dev/null +++ b/UITests/Sources/__Snapshots__/Application/fr-FR-iPad-9th-generation.roomSmallTimelineIncomingAndSmallPagination.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:087309a5c640fe574e3f80e9e8cbccef71bced0d49016a27c963d7bff07e24d1 +size 133159 diff --git a/UITests/Sources/__Snapshots__/Application/fr-FR-iPhone-14.roomSmallTimeline.png b/UITests/Sources/__Snapshots__/Application/fr-FR-iPhone-14.roomSmallTimeline.png new file mode 100644 index 000000000..dde9c7b59 --- /dev/null +++ b/UITests/Sources/__Snapshots__/Application/fr-FR-iPhone-14.roomSmallTimeline.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:180aae2e7f67beb59cfee31788efaf7f0a6b4fef910956ba1db1ed22edbfdc7f +size 114685 diff --git a/UITests/Sources/__Snapshots__/Application/fr-FR-iPhone-14.roomSmallTimelineIncomingAndSmallPagination.png b/UITests/Sources/__Snapshots__/Application/fr-FR-iPhone-14.roomSmallTimelineIncomingAndSmallPagination.png new file mode 100644 index 000000000..9475fb9fc --- /dev/null +++ b/UITests/Sources/__Snapshots__/Application/fr-FR-iPhone-14.roomSmallTimelineIncomingAndSmallPagination.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:31267576b8c4c8d038b3f5dde9e3f6c1f06c00b72cea3e5e9d02765e231fa682 +size 212873 diff --git a/changelog.d/352.change b/changelog.d/352.change new file mode 100644 index 000000000..060f34f22 --- /dev/null +++ b/changelog.d/352.change @@ -0,0 +1 @@ +Timeline: Add a couple of basic tests to make sure the timeline is bottom aligned.