mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-05 20:09:04 +00:00
Fix UI tests hanging. (#429)
* Only use Client for UI test signalling. Make the server listen and return a client. * Refactor to Listener and Connection. Matches Network.framework naming. * Wait until the connection is ready before setting the established connection. * Docs and fix method name. * Fix CI issues. - Use Bonjour to allow multiple devices running simultaneously. - Sleep after establishing the connection to fix issues on Intel.
This commit is contained in:
@@ -3,7 +3,7 @@
|
|||||||
archiveVersion = 1;
|
archiveVersion = 1;
|
||||||
classes = {
|
classes = {
|
||||||
};
|
};
|
||||||
objectVersion = 51;
|
objectVersion = 54;
|
||||||
objects = {
|
objects = {
|
||||||
|
|
||||||
/* Begin PBXBuildFile section */
|
/* Begin PBXBuildFile section */
|
||||||
@@ -175,7 +175,7 @@
|
|||||||
5B8B51CEC4717AF487794685 /* NotificationServiceProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B490675B8E31423AF116BDA /* NotificationServiceProxy.swift */; };
|
5B8B51CEC4717AF487794685 /* NotificationServiceProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B490675B8E31423AF116BDA /* NotificationServiceProxy.swift */; };
|
||||||
5C02841B2A86327B2C377682 /* NotificationConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = C830A64609CBD152F06E0457 /* NotificationConstants.swift */; };
|
5C02841B2A86327B2C377682 /* NotificationConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = C830A64609CBD152F06E0457 /* NotificationConstants.swift */; };
|
||||||
5C8AFBF168A41E20835F3B86 /* LoginScreenUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1DB34B0C74CD242FED9DD069 /* LoginScreenUITests.swift */; };
|
5C8AFBF168A41E20835F3B86 /* LoginScreenUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1DB34B0C74CD242FED9DD069 /* LoginScreenUITests.swift */; };
|
||||||
5D1C6D4E3583700DF7A64834 /* UITestSignalling.swift in Sources */ = {isa = PBXBuildFile; fileRef = D619626288AAB513B7620BB6 /* UITestSignalling.swift */; };
|
5D1C6D4E3583700DF7A64834 /* UITestsSignalling.swift in Sources */ = {isa = PBXBuildFile; fileRef = D619626288AAB513B7620BB6 /* UITestsSignalling.swift */; };
|
||||||
5D2AF8C0DF872E7985F8FE54 /* TimelineDeliveryStatusView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5AC06FC11B6638F7BF1670E /* TimelineDeliveryStatusView.swift */; };
|
5D2AF8C0DF872E7985F8FE54 /* TimelineDeliveryStatusView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5AC06FC11B6638F7BF1670E /* TimelineDeliveryStatusView.swift */; };
|
||||||
5D430CDE11EAC3E8E6B80A66 /* RoomTimelineViewFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3FEE631F3A4AFDC6652DD9DA /* RoomTimelineViewFactory.swift */; };
|
5D430CDE11EAC3E8E6B80A66 /* RoomTimelineViewFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3FEE631F3A4AFDC6652DD9DA /* RoomTimelineViewFactory.swift */; };
|
||||||
5D70FAE4D2BF4553AFFFFE41 /* NotificationItemProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 25F7FE40EF7490A7E09D7BE6 /* NotificationItemProxy.swift */; };
|
5D70FAE4D2BF4553AFFFFE41 /* NotificationItemProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 25F7FE40EF7490A7E09D7BE6 /* NotificationItemProxy.swift */; };
|
||||||
@@ -467,7 +467,7 @@
|
|||||||
F6E860FF7B18B81DF43B30B8 /* EncryptedRoomTimelineItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3FA7C8D4EF2B1873C180ED7 /* EncryptedRoomTimelineItem.swift */; };
|
F6E860FF7B18B81DF43B30B8 /* EncryptedRoomTimelineItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3FA7C8D4EF2B1873C180ED7 /* EncryptedRoomTimelineItem.swift */; };
|
||||||
F6F49E37272AD7397CD29A01 /* HomeScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 505208F28007C0FEC14E1FF0 /* HomeScreenViewModelTests.swift */; };
|
F6F49E37272AD7397CD29A01 /* HomeScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 505208F28007C0FEC14E1FF0 /* HomeScreenViewModelTests.swift */; };
|
||||||
F7567DD6635434E8C563BF85 /* AnalyticsClientProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = E3B97591B2D3D4D67553506D /* AnalyticsClientProtocol.swift */; };
|
F7567DD6635434E8C563BF85 /* AnalyticsClientProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = E3B97591B2D3D4D67553506D /* AnalyticsClientProtocol.swift */; };
|
||||||
F98333D53D85B0E029FDCA12 /* UITestSignalling.swift in Sources */ = {isa = PBXBuildFile; fileRef = D619626288AAB513B7620BB6 /* UITestSignalling.swift */; };
|
F98333D53D85B0E029FDCA12 /* UITestsSignalling.swift in Sources */ = {isa = PBXBuildFile; fileRef = D619626288AAB513B7620BB6 /* UITestsSignalling.swift */; };
|
||||||
F9981191DC408AED537C1749 /* MediaProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = E12C9E0B61A77C7F0EE7918C /* MediaProxy.swift */; };
|
F9981191DC408AED537C1749 /* MediaProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = E12C9E0B61A77C7F0EE7918C /* MediaProxy.swift */; };
|
||||||
F99FB21EFC6D99D247FE7CBE /* Kingfisher in Frameworks */ = {isa = PBXBuildFile; productRef = DE8DC9B3FBA402117DC4C49F /* Kingfisher */; };
|
F99FB21EFC6D99D247FE7CBE /* Kingfisher in Frameworks */ = {isa = PBXBuildFile; productRef = DE8DC9B3FBA402117DC4C49F /* Kingfisher */; };
|
||||||
F9F6D2883BBEBB9A3789A137 /* OnboardingViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00A941F289F6AB876BA3361A /* OnboardingViewModelTests.swift */; };
|
F9F6D2883BBEBB9A3789A137 /* OnboardingViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00A941F289F6AB876BA3361A /* OnboardingViewModelTests.swift */; };
|
||||||
@@ -811,7 +811,7 @@
|
|||||||
8D6094DEAAEB388E1AE118C6 /* MockRoomTimelineProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockRoomTimelineProvider.swift; sourceTree = "<group>"; };
|
8D6094DEAAEB388E1AE118C6 /* MockRoomTimelineProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockRoomTimelineProvider.swift; sourceTree = "<group>"; };
|
||||||
8D8169443E5AC5FF71BFB3DB /* cs */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = cs; path = cs.lproj/Localizable.strings; sourceTree = "<group>"; };
|
8D8169443E5AC5FF71BFB3DB /* cs */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = cs; path = cs.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||||
8DC2C9E0E15C79BBDA80F0A2 /* TimelineStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineStyle.swift; sourceTree = "<group>"; };
|
8DC2C9E0E15C79BBDA80F0A2 /* TimelineStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineStyle.swift; sourceTree = "<group>"; };
|
||||||
8E088F2A1B9EC529D3221931 /* UITests.xctestplan */ = {isa = PBXFileReference; path = UITests.xctestplan; sourceTree = "<group>"; };
|
8E088F2A1B9EC529D3221931 /* UITests.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = UITests.xctestplan; sourceTree = "<group>"; };
|
||||||
8ED2D2F6A137A95EA50413BE /* UserNotificationControllerProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserNotificationControllerProtocol.swift; sourceTree = "<group>"; };
|
8ED2D2F6A137A95EA50413BE /* UserNotificationControllerProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserNotificationControllerProtocol.swift; sourceTree = "<group>"; };
|
||||||
8F7D42E66E939B709C1EC390 /* MockRoomSummaryProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockRoomSummaryProvider.swift; sourceTree = "<group>"; };
|
8F7D42E66E939B709C1EC390 /* MockRoomSummaryProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockRoomSummaryProvider.swift; sourceTree = "<group>"; };
|
||||||
8FC26871038FB0E4AAE22605 /* apple_emojis_data.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = apple_emojis_data.json; sourceTree = "<group>"; };
|
8FC26871038FB0E4AAE22605 /* apple_emojis_data.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = apple_emojis_data.json; sourceTree = "<group>"; };
|
||||||
@@ -977,7 +977,7 @@
|
|||||||
D3D455BC2423D911A62ACFB2 /* NSELogger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSELogger.swift; sourceTree = "<group>"; };
|
D3D455BC2423D911A62ACFB2 /* NSELogger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSELogger.swift; sourceTree = "<group>"; };
|
||||||
D4DA544B2520BFA65D6DB4BB /* target.yml */ = {isa = PBXFileReference; lastKnownFileType = text.yaml; path = target.yml; sourceTree = "<group>"; };
|
D4DA544B2520BFA65D6DB4BB /* target.yml */ = {isa = PBXFileReference; lastKnownFileType = text.yaml; path = target.yml; sourceTree = "<group>"; };
|
||||||
D5AC06FC11B6638F7BF1670E /* TimelineDeliveryStatusView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineDeliveryStatusView.swift; sourceTree = "<group>"; };
|
D5AC06FC11B6638F7BF1670E /* TimelineDeliveryStatusView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineDeliveryStatusView.swift; sourceTree = "<group>"; };
|
||||||
D619626288AAB513B7620BB6 /* UITestSignalling.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UITestSignalling.swift; sourceTree = "<group>"; };
|
D619626288AAB513B7620BB6 /* UITestsSignalling.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UITestsSignalling.swift; sourceTree = "<group>"; };
|
||||||
D653265D006E708E4E51AD64 /* HomeScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeScreenCoordinator.swift; sourceTree = "<group>"; };
|
D653265D006E708E4E51AD64 /* HomeScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeScreenCoordinator.swift; sourceTree = "<group>"; };
|
||||||
D67CBAFA48ED0B6FCE74F88F /* lt */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = lt; path = lt.lproj/Localizable.strings; sourceTree = "<group>"; };
|
D67CBAFA48ED0B6FCE74F88F /* lt */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = lt; path = lt.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||||
D6CA5F386C7701C129398945 /* AuthenticationCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthenticationCoordinator.swift; sourceTree = "<group>"; };
|
D6CA5F386C7701C129398945 /* AuthenticationCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthenticationCoordinator.swift; sourceTree = "<group>"; };
|
||||||
@@ -1027,7 +1027,7 @@
|
|||||||
EBE5502760CF6CA2D7201883 /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = ja; path = ja.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
|
EBE5502760CF6CA2D7201883 /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = ja; path = ja.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
|
||||||
ED044D00F2176681CC02CD54 /* HomeScreenRoomCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeScreenRoomCell.swift; sourceTree = "<group>"; };
|
ED044D00F2176681CC02CD54 /* HomeScreenRoomCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeScreenRoomCell.swift; sourceTree = "<group>"; };
|
||||||
ED1D792EB82506A19A72C8DE /* RoomTimelineItemProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomTimelineItemProtocol.swift; sourceTree = "<group>"; };
|
ED1D792EB82506A19A72C8DE /* RoomTimelineItemProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomTimelineItemProtocol.swift; sourceTree = "<group>"; };
|
||||||
ED482057AE39D5C6D9C5F3D8 /* message.caf */ = {isa = PBXFileReference; path = message.caf; sourceTree = "<group>"; };
|
ED482057AE39D5C6D9C5F3D8 /* message.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = message.caf; sourceTree = "<group>"; };
|
||||||
EDAA4472821985BF868CC21C /* ServerSelectionViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerSelectionViewModelTests.swift; sourceTree = "<group>"; };
|
EDAA4472821985BF868CC21C /* ServerSelectionViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerSelectionViewModelTests.swift; sourceTree = "<group>"; };
|
||||||
EDB6E40BAD4504D899FAAC9A /* TemplateViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TemplateViewModel.swift; sourceTree = "<group>"; };
|
EDB6E40BAD4504D899FAAC9A /* TemplateViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TemplateViewModel.swift; sourceTree = "<group>"; };
|
||||||
EE8BCD14EFED23459A43FDFF /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/Localizable.strings; sourceTree = "<group>"; };
|
EE8BCD14EFED23459A43FDFF /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||||
@@ -1277,7 +1277,7 @@
|
|||||||
children = (
|
children = (
|
||||||
46C208DA43CE25D13E670F40 /* UITestsAppCoordinator.swift */,
|
46C208DA43CE25D13E670F40 /* UITestsAppCoordinator.swift */,
|
||||||
CC6FE34A0A47D010BBB4D4D4 /* UITestScreenIdentifier.swift */,
|
CC6FE34A0A47D010BBB4D4D4 /* UITestScreenIdentifier.swift */,
|
||||||
D619626288AAB513B7620BB6 /* UITestSignalling.swift */,
|
D619626288AAB513B7620BB6 /* UITestsSignalling.swift */,
|
||||||
D751BB69BB7C38FD247517B4 /* UITestsRootCoordinator.swift */,
|
D751BB69BB7C38FD247517B4 /* UITestsRootCoordinator.swift */,
|
||||||
);
|
);
|
||||||
path = UITests;
|
path = UITests;
|
||||||
@@ -3228,7 +3228,7 @@
|
|||||||
706F79A39BDB32F592B8C2C7 /* UIKitBackgroundTask.swift in Sources */,
|
706F79A39BDB32F592B8C2C7 /* UIKitBackgroundTask.swift in Sources */,
|
||||||
3097A0A867D2B19CE32DAE58 /* UIKitBackgroundTaskService.swift in Sources */,
|
3097A0A867D2B19CE32DAE58 /* UIKitBackgroundTaskService.swift in Sources */,
|
||||||
D05A193AE63030F2CFCE2E9C /* UITestScreenIdentifier.swift in Sources */,
|
D05A193AE63030F2CFCE2E9C /* UITestScreenIdentifier.swift in Sources */,
|
||||||
5D1C6D4E3583700DF7A64834 /* UITestSignalling.swift in Sources */,
|
5D1C6D4E3583700DF7A64834 /* UITestsSignalling.swift in Sources */,
|
||||||
E96005321849DBD7C72A28F2 /* UITestsAppCoordinator.swift in Sources */,
|
E96005321849DBD7C72A28F2 /* UITestsAppCoordinator.swift in Sources */,
|
||||||
086C2FA7750378EB2BFD0BEE /* UITestsRootCoordinator.swift in Sources */,
|
086C2FA7750378EB2BFD0BEE /* UITestsRootCoordinator.swift in Sources */,
|
||||||
071A017E415AD378F2961B11 /* URL.swift in Sources */,
|
071A017E415AD378F2961B11 /* URL.swift in Sources */,
|
||||||
@@ -3283,7 +3283,7 @@
|
|||||||
B3357B00F1AA930E54F76609 /* Strings.swift in Sources */,
|
B3357B00F1AA930E54F76609 /* Strings.swift in Sources */,
|
||||||
C4180F418235DAD9DD173951 /* TemplateScreenUITests.swift in Sources */,
|
C4180F418235DAD9DD173951 /* TemplateScreenUITests.swift in Sources */,
|
||||||
9A47B7EFE3793760EEF68FFE /* UITestScreenIdentifier.swift in Sources */,
|
9A47B7EFE3793760EEF68FFE /* UITestScreenIdentifier.swift in Sources */,
|
||||||
F98333D53D85B0E029FDCA12 /* UITestSignalling.swift in Sources */,
|
F98333D53D85B0E029FDCA12 /* UITestsSignalling.swift in Sources */,
|
||||||
B22D857D1E8FCA6DD74A58E3 /* UserSessionScreenTests.swift in Sources */,
|
B22D857D1E8FCA6DD74A58E3 /* UserSessionScreenTests.swift in Sources */,
|
||||||
588411C8FD72B2A2DFE5F7DE /* XCUIElement.swift in Sources */,
|
588411C8FD72B2A2DFE5F7DE /* XCUIElement.swift in Sources */,
|
||||||
);
|
);
|
||||||
|
|||||||
+9
-9
@@ -32,11 +32,11 @@ class MockRoomTimelineController: RoomTimelineControllerProtocol {
|
|||||||
|
|
||||||
var timelineItems: [RoomTimelineItemProtocol] = RoomTimelineItemFixtures.default
|
var timelineItems: [RoomTimelineItemProtocol] = RoomTimelineItemFixtures.default
|
||||||
|
|
||||||
private var client: UITestsSignalling.Client?
|
private var connection: UITestsSignalling.Connection?
|
||||||
|
|
||||||
init(listenForSignals: Bool = false) {
|
init(listenForSignals: Bool = false) {
|
||||||
if listenForSignals {
|
if listenForSignals {
|
||||||
client = .init()
|
connection = .init()
|
||||||
Task { try await startListening() }
|
Task { try await startListening() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -85,15 +85,15 @@ class MockRoomTimelineController: RoomTimelineControllerProtocol {
|
|||||||
|
|
||||||
/// Allows the simulation of server responses by listening for signals from UI tests.
|
/// Allows the simulation of server responses by listening for signals from UI tests.
|
||||||
private func startListening() async throws {
|
private func startListening() async throws {
|
||||||
try await client?.connect()
|
try await connection?.connect()
|
||||||
|
|
||||||
Task {
|
Task {
|
||||||
while let client {
|
while let connection {
|
||||||
do {
|
do {
|
||||||
try await handleSignal(client.receive())
|
try await handleSignal(connection.receive())
|
||||||
} catch {
|
} catch {
|
||||||
client.disconnect()
|
connection.disconnect()
|
||||||
self.client = nil
|
self.connection = nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -119,7 +119,7 @@ class MockRoomTimelineController: RoomTimelineControllerProtocol {
|
|||||||
timelineItems.append(incomingItem)
|
timelineItems.append(incomingItem)
|
||||||
callbacks.send(.updatedTimelineItems)
|
callbacks.send(.updatedTimelineItems)
|
||||||
|
|
||||||
try? await client?.send(.success)
|
try? await connection?.send(.success)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Prepends the next chunk of items to the `timelineItems` array.
|
/// Prepends the next chunk of items to the `timelineItems` array.
|
||||||
@@ -131,6 +131,6 @@ class MockRoomTimelineController: RoomTimelineControllerProtocol {
|
|||||||
callbacks.send(.updatedTimelineItems)
|
callbacks.send(.updatedTimelineItems)
|
||||||
callbacks.send(.finishedBackPaginating)
|
callbacks.send(.finishedBackPaginating)
|
||||||
|
|
||||||
try? await client?.send(.success)
|
try? await connection?.send(.success)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,191 +0,0 @@
|
|||||||
//
|
|
||||||
// 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
|
|
||||||
import Network
|
|
||||||
|
|
||||||
enum UITestsSignal: String {
|
|
||||||
/// An internal signal used to bring up the connection.
|
|
||||||
case connect
|
|
||||||
/// Ask the app to back paginate.
|
|
||||||
case paginate
|
|
||||||
/// Ask the app to simulate an incoming message.
|
|
||||||
case incomingMessage
|
|
||||||
/// The operation has completed successfully.
|
|
||||||
case success
|
|
||||||
}
|
|
||||||
|
|
||||||
enum UITestsSignalError: Error {
|
|
||||||
/// An unknown error occurred.
|
|
||||||
case unknown
|
|
||||||
/// Signalling could not be used as is hasn't been enabled.
|
|
||||||
case disabled
|
|
||||||
/// The connection was cancelled.
|
|
||||||
case cancelled
|
|
||||||
/// The connection hasn't been established.
|
|
||||||
case notConnected
|
|
||||||
/// Attempted to receive multiple signals at once.
|
|
||||||
case awaitingAnotherSignal
|
|
||||||
/// A network error occurred.
|
|
||||||
case nwError(NWError)
|
|
||||||
/// An unexpected signal was received. This error isn't used internally.
|
|
||||||
case unexpected
|
|
||||||
}
|
|
||||||
|
|
||||||
enum UITestsSignalling {
|
|
||||||
/// A UDP server that can be used for signalling between the UI tests jig and the app.
|
|
||||||
/// The server should be instantiated on the UI tests side.
|
|
||||||
class Server: UITestsSignallingProtocol {
|
|
||||||
private let listener: NWListener
|
|
||||||
|
|
||||||
var connection: NWConnection
|
|
||||||
var nextMessageContinuation: CheckedContinuation<UITestsSignal, Error>?
|
|
||||||
|
|
||||||
/// Creates a new signalling server.
|
|
||||||
init() throws {
|
|
||||||
connection = NWConnection(host: "127.0.0.1", port: 0, using: .udp)
|
|
||||||
listener = try NWListener(using: .udp, on: 1234)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Listens for a client and attempts to negotiate a connection when one is discovered.
|
|
||||||
func connect() async throws {
|
|
||||||
guard listener.state == .setup else { return }
|
|
||||||
|
|
||||||
return try await withCheckedThrowingContinuation { [weak self] continuation in
|
|
||||||
listener.newConnectionHandler = { connection in
|
|
||||||
connection.start(queue: .main)
|
|
||||||
connection.stateUpdateHandler = { state in
|
|
||||||
switch state {
|
|
||||||
case .ready:
|
|
||||||
self?.listener.cancel()
|
|
||||||
self?.receiveNextMessage()
|
|
||||||
continuation.resume()
|
|
||||||
case .failed(let error):
|
|
||||||
self?.listener.cancel()
|
|
||||||
continuation.resume(with: .failure(error))
|
|
||||||
default:
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self?.connection = connection
|
|
||||||
}
|
|
||||||
listener.start(queue: .main)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Stops the connection (or the listener if a connection hasn't been established).
|
|
||||||
func disconnect() {
|
|
||||||
listener.cancel()
|
|
||||||
connection.cancel()
|
|
||||||
if let nextMessageContinuation {
|
|
||||||
nextMessageContinuation.resume(throwing: UITestsSignalError.cancelled)
|
|
||||||
self.nextMessageContinuation = nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A UDP client that can be used for signalling between the app and the UI tests jig.
|
|
||||||
/// The client should be instantiated on the app side.
|
|
||||||
class Client: UITestsSignallingProtocol {
|
|
||||||
let connection: NWConnection
|
|
||||||
var nextMessageContinuation: CheckedContinuation<UITestsSignal, Error>?
|
|
||||||
|
|
||||||
/// Creates a new signalling client.
|
|
||||||
init() {
|
|
||||||
connection = NWConnection(host: "127.0.0.1", port: 1234, using: .udp)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Attempts to connect to a server.
|
|
||||||
func connect() async throws {
|
|
||||||
guard connection.state == .setup else { return }
|
|
||||||
|
|
||||||
return try await withCheckedThrowingContinuation { continuation in
|
|
||||||
connection.start(queue: .main)
|
|
||||||
connection.stateUpdateHandler = { state in
|
|
||||||
switch state {
|
|
||||||
case .ready:
|
|
||||||
self.receiveNextMessage()
|
|
||||||
continuation.resume()
|
|
||||||
Task { try await self.send(.connect) }
|
|
||||||
case .failed(let error):
|
|
||||||
continuation.resume(with: .failure(error))
|
|
||||||
default:
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Stops the connection.
|
|
||||||
func disconnect() {
|
|
||||||
connection.cancel()
|
|
||||||
if let nextMessageContinuation {
|
|
||||||
nextMessageContinuation.resume(throwing: UITestsSignalError.cancelled)
|
|
||||||
self.nextMessageContinuation = nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A shared implementation for sending/receiving signals between a client and a server.
|
|
||||||
protocol UITestsSignallingProtocol: AnyObject {
|
|
||||||
var connection: NWConnection { get }
|
|
||||||
var nextMessageContinuation: CheckedContinuation<UITestsSignal, Error>? { get set }
|
|
||||||
}
|
|
||||||
|
|
||||||
extension UITestsSignallingProtocol {
|
|
||||||
/// Sends a message to the connected client/server.
|
|
||||||
func send(_ signal: UITestsSignal) async throws {
|
|
||||||
guard connection.state == .ready else { throw UITestsSignalError.notConnected }
|
|
||||||
let data = signal.rawValue.data(using: .utf8)
|
|
||||||
connection.send(content: data, completion: .idempotent)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the next message received by the client/server.
|
|
||||||
func receive() async throws -> UITestsSignal {
|
|
||||||
guard connection.state == .ready else { throw UITestsSignalError.notConnected }
|
|
||||||
guard nextMessageContinuation == nil else { throw UITestsSignalError.awaitingAnotherSignal }
|
|
||||||
|
|
||||||
return try await withCheckedThrowingContinuation { continuation in
|
|
||||||
self.nextMessageContinuation = continuation
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Processes the next message received by the client/server
|
|
||||||
fileprivate func receiveNextMessage() {
|
|
||||||
connection.receiveMessage { [weak self] completeContent, _, isComplete, error in
|
|
||||||
guard let self else { return }
|
|
||||||
guard isComplete else { fatalError("Partial messages not supported") }
|
|
||||||
|
|
||||||
guard let completeContent,
|
|
||||||
let message = String(data: completeContent, encoding: .utf8),
|
|
||||||
let signal = UITestsSignal(rawValue: message)
|
|
||||||
else {
|
|
||||||
let error: UITestsSignalError = error.map { .nwError($0) } ?? .unknown
|
|
||||||
self.nextMessageContinuation?.resume(with: .failure(error))
|
|
||||||
self.nextMessageContinuation = nil
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if signal != .connect {
|
|
||||||
self.nextMessageContinuation?.resume(returning: signal)
|
|
||||||
self.nextMessageContinuation = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
self.receiveNextMessage()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,213 @@
|
|||||||
|
//
|
||||||
|
// 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 Network
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
enum UITestsSignal: String {
|
||||||
|
/// An internal signal used to bring up the connection.
|
||||||
|
case connect
|
||||||
|
/// Ask the app to back paginate.
|
||||||
|
case paginate
|
||||||
|
/// Ask the app to simulate an incoming message.
|
||||||
|
case incomingMessage
|
||||||
|
/// The operation has completed successfully.
|
||||||
|
case success
|
||||||
|
}
|
||||||
|
|
||||||
|
enum UITestsSignalError: Error {
|
||||||
|
/// An unknown error occurred.
|
||||||
|
case unknown
|
||||||
|
/// Signalling could not be used as is hasn't been enabled.
|
||||||
|
case disabled
|
||||||
|
/// The connection was cancelled.
|
||||||
|
case cancelled
|
||||||
|
/// The connection hasn't been established.
|
||||||
|
case notConnected
|
||||||
|
/// Attempted to receive multiple signals at once.
|
||||||
|
case awaitingAnotherSignal
|
||||||
|
/// A network error occurred.
|
||||||
|
case nwError(NWError)
|
||||||
|
/// An unexpected signal was received. This error isn't used internally.
|
||||||
|
case unexpected
|
||||||
|
}
|
||||||
|
|
||||||
|
enum UITestsSignalling {
|
||||||
|
/// The Bonjour service name used for the connection. The device name
|
||||||
|
/// is included to allow UI tests to run on multiple devices simultaneously.
|
||||||
|
private static let serviceName = "UITestsSignalling \(UIDevice.current.name)"
|
||||||
|
/// The Bonjour service type used for the connection.
|
||||||
|
private static let serviceType = "_signalling._udp."
|
||||||
|
/// The Bonjour domain used for the connection.
|
||||||
|
private static let domain = "local."
|
||||||
|
/// The DispatchQueue used for networking.
|
||||||
|
private static let queue: DispatchQueue = .main
|
||||||
|
|
||||||
|
/// A network listener that can be used in the UI tests runner to create a two-way `Connection` with the app.
|
||||||
|
class Listener {
|
||||||
|
/// The underlying network listener.
|
||||||
|
private let listener: NWListener
|
||||||
|
|
||||||
|
/// The established connection. This is stored in case the connection is established
|
||||||
|
/// before `connection()` is awaited and so the continuation is still `nil`.
|
||||||
|
private var establishedConnection: Connection?
|
||||||
|
/// The continuation to call when a connection is established.
|
||||||
|
private var connectionContinuation: CheckedContinuation<Connection, Error>?
|
||||||
|
|
||||||
|
/// Creates a new signalling `Listener` and starts listening.
|
||||||
|
init() throws {
|
||||||
|
let service = NWListener.Service(name: UITestsSignalling.serviceName, type: UITestsSignalling.serviceType, domain: UITestsSignalling.domain)
|
||||||
|
listener = try NWListener(service: service, using: .udp)
|
||||||
|
listener.newConnectionHandler = { [weak self] nwConnection in
|
||||||
|
let connection = Connection(nwConnection: nwConnection)
|
||||||
|
nwConnection.start(queue: UITestsSignalling.queue)
|
||||||
|
nwConnection.stateUpdateHandler = { state in
|
||||||
|
switch state {
|
||||||
|
case .ready:
|
||||||
|
connection.receiveNextMessage()
|
||||||
|
self?.establishedConnection = connection
|
||||||
|
self?.connectionContinuation?.resume(returning: connection)
|
||||||
|
case .failed(let error):
|
||||||
|
self?.connectionContinuation?.resume(with: .failure(error))
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self?.listener.cancel() // Stop listening for connections when one is discovered.
|
||||||
|
}
|
||||||
|
listener.start(queue: UITestsSignalling.queue)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the negotiated `Connection` as and when it has been established.
|
||||||
|
func connection() async throws -> Connection {
|
||||||
|
guard listener.state == .setup else { throw UITestsSignalError.unknown }
|
||||||
|
if let establishedConnection {
|
||||||
|
return establishedConnection
|
||||||
|
}
|
||||||
|
return try await withCheckedThrowingContinuation { [weak self] continuation in
|
||||||
|
self?.connectionContinuation = continuation
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Stops the listening when a connection hasn't been established.
|
||||||
|
func cancel() {
|
||||||
|
listener.cancel()
|
||||||
|
if let connectionContinuation {
|
||||||
|
connectionContinuation.resume(throwing: UITestsSignalError.cancelled)
|
||||||
|
self.connectionContinuation = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A two-way UDP connection that can be used for signalling between the app and the UI tests runner.
|
||||||
|
/// The connection should be created as follows:
|
||||||
|
/// - Create a `Listener` in the UI tests before launching the app. This will automatically start listening for a connection.
|
||||||
|
/// - With in the App, create a `Connection` and call `connect()` to establish a connection.
|
||||||
|
/// - Await the `connection()` on the `Listener` when you need to send the signal.
|
||||||
|
/// - The two `Connection` objects can now be used for two-way signalling.
|
||||||
|
class Connection {
|
||||||
|
/// The underlying network connection.
|
||||||
|
private let connection: NWConnection
|
||||||
|
/// A continuation to call each time a signal is received.
|
||||||
|
private var nextMessageContinuation: CheckedContinuation<UITestsSignal, Error>?
|
||||||
|
|
||||||
|
/// Creates a new signalling `Connection`.
|
||||||
|
init() {
|
||||||
|
let endpoint = NWEndpoint.service(name: UITestsSignalling.serviceName,
|
||||||
|
type: UITestsSignalling.serviceType,
|
||||||
|
domain: UITestsSignalling.domain,
|
||||||
|
interface: nil)
|
||||||
|
connection = NWConnection(to: endpoint, using: .udp)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a new signalling `Connection` from an established `NWConnection`.
|
||||||
|
fileprivate init(nwConnection: NWConnection) {
|
||||||
|
connection = nwConnection
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Attempts to establish a connection with a `Listener`.
|
||||||
|
func connect() async throws {
|
||||||
|
guard connection.state == .setup else { return }
|
||||||
|
|
||||||
|
return try await withCheckedThrowingContinuation { continuation in
|
||||||
|
connection.start(queue: UITestsSignalling.queue)
|
||||||
|
connection.stateUpdateHandler = { state in
|
||||||
|
switch state {
|
||||||
|
case .ready:
|
||||||
|
self.receiveNextMessage()
|
||||||
|
continuation.resume()
|
||||||
|
Task { try await self.send(.connect) }
|
||||||
|
case .failed(let error):
|
||||||
|
continuation.resume(with: .failure(error))
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Stops the connection.
|
||||||
|
func disconnect() {
|
||||||
|
connection.cancel()
|
||||||
|
if let nextMessageContinuation {
|
||||||
|
nextMessageContinuation.resume(throwing: UITestsSignalError.cancelled)
|
||||||
|
self.nextMessageContinuation = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sends a message to the other side of the connection.
|
||||||
|
func send(_ signal: UITestsSignal) async throws {
|
||||||
|
guard connection.state == .ready else { throw UITestsSignalError.notConnected }
|
||||||
|
let data = signal.rawValue.data(using: .utf8)
|
||||||
|
connection.send(content: data, completion: .idempotent)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the next message received from the other side of the connection.
|
||||||
|
func receive() async throws -> UITestsSignal {
|
||||||
|
guard connection.state == .ready else { throw UITestsSignalError.notConnected }
|
||||||
|
guard nextMessageContinuation == nil else { throw UITestsSignalError.awaitingAnotherSignal }
|
||||||
|
|
||||||
|
return try await withCheckedThrowingContinuation { continuation in
|
||||||
|
self.nextMessageContinuation = continuation
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Processes the next message received by the connection.
|
||||||
|
fileprivate func receiveNextMessage() {
|
||||||
|
connection.receiveMessage { [weak self] completeContent, _, isComplete, error in
|
||||||
|
guard let self else { return }
|
||||||
|
guard isComplete else { fatalError("Partial messages not supported") }
|
||||||
|
|
||||||
|
guard let completeContent,
|
||||||
|
let message = String(data: completeContent, encoding: .utf8),
|
||||||
|
let signal = UITestsSignal(rawValue: message)
|
||||||
|
else {
|
||||||
|
let error: UITestsSignalError = error.map { .nwError($0) } ?? .unknown
|
||||||
|
self.nextMessageContinuation?.resume(with: .failure(error))
|
||||||
|
self.nextMessageContinuation = nil
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if signal != .connect {
|
||||||
|
self.nextMessageContinuation?.resume(returning: signal)
|
||||||
|
self.nextMessageContinuation = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
self.receiveNextMessage()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -48,46 +48,49 @@ class RoomScreenUITests: XCTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func testSmallTimelineWithIncomingAndPagination() async throws {
|
func testSmallTimelineWithIncomingAndPagination() async throws {
|
||||||
let server = try UITestsSignalling.Server()
|
let listener = try UITestsSignalling.Listener()
|
||||||
|
|
||||||
let app = Application.launch()
|
let app = Application.launch()
|
||||||
app.goToScreenWithIdentifier(.roomSmallTimelineIncomingAndSmallPagination)
|
app.goToScreenWithIdentifier(.roomSmallTimelineIncomingAndSmallPagination)
|
||||||
|
|
||||||
try await server.connect()
|
let connection = try await listener.connection()
|
||||||
defer { server.disconnect() }
|
try await Task.sleep(for: .seconds(1)) // Allow the connection to settle on CI/Intel...
|
||||||
|
defer { connection.disconnect() }
|
||||||
|
|
||||||
// When a back pagination occurs and an incoming message arrives.
|
// When a back pagination occurs and an incoming message arrives.
|
||||||
try await performOperation(.incomingMessage, using: server)
|
try await performOperation(.incomingMessage, using: connection)
|
||||||
try await performOperation(.paginate, using: server)
|
try await performOperation(.paginate, using: connection)
|
||||||
|
|
||||||
// Then the 4 visible messages should stay aligned to the bottom.
|
// Then the 4 visible messages should stay aligned to the bottom.
|
||||||
app.assertScreenshot(.roomSmallTimelineIncomingAndSmallPagination)
|
app.assertScreenshot(.roomSmallTimelineIncomingAndSmallPagination)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testSmallTimelineWithLargePagination() async throws {
|
func testSmallTimelineWithLargePagination() async throws {
|
||||||
let server = try UITestsSignalling.Server()
|
let listener = try UITestsSignalling.Listener()
|
||||||
|
|
||||||
let app = Application.launch()
|
let app = Application.launch()
|
||||||
app.goToScreenWithIdentifier(.roomSmallTimelineLargePagination)
|
app.goToScreenWithIdentifier(.roomSmallTimelineLargePagination)
|
||||||
|
|
||||||
try await server.connect()
|
let connection = try await listener.connection()
|
||||||
defer { server.disconnect() }
|
try await Task.sleep(for: .seconds(1)) // Allow the connection to settle on CI/Intel...
|
||||||
|
defer { connection.disconnect() }
|
||||||
|
|
||||||
// When a large back pagination occurs.
|
// When a large back pagination occurs.
|
||||||
try await performOperation(.paginate, using: server)
|
try await performOperation(.paginate, using: connection)
|
||||||
|
|
||||||
// The bottom of the timeline should remain visible with more items added above.
|
// The bottom of the timeline should remain visible with more items added above.
|
||||||
app.assertScreenshot(.roomSmallTimelineLargePagination)
|
app.assertScreenshot(.roomSmallTimelineLargePagination)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testTimelineLayoutInMiddle() async throws {
|
func testTimelineLayoutInMiddle() async throws {
|
||||||
let server = try UITestsSignalling.Server()
|
let listener = try UITestsSignalling.Listener()
|
||||||
|
|
||||||
let app = Application.launch()
|
let app = Application.launch()
|
||||||
app.goToScreenWithIdentifier(.roomLayoutMiddle)
|
app.goToScreenWithIdentifier(.roomLayoutMiddle)
|
||||||
|
|
||||||
try await server.connect()
|
let connection = try await listener.connection()
|
||||||
defer { server.disconnect() }
|
try await Task.sleep(for: .seconds(1)) // Allow the connection to settle on CI/Intel...
|
||||||
|
defer { connection.disconnect() }
|
||||||
|
|
||||||
// Given a timeline that is neither at the top nor the bottom.
|
// Given a timeline that is neither at the top nor the bottom.
|
||||||
app.tables.element.swipeDown()
|
app.tables.element.swipeDown()
|
||||||
@@ -95,13 +98,13 @@ class RoomScreenUITests: XCTestCase {
|
|||||||
app.assertScreenshot(.roomLayoutMiddle, step: 0) // Assert initial state for comparison.
|
app.assertScreenshot(.roomLayoutMiddle, step: 0) // Assert initial state for comparison.
|
||||||
|
|
||||||
// When a back pagination occurs.
|
// When a back pagination occurs.
|
||||||
try await performOperation(.paginate, using: server)
|
try await performOperation(.paginate, using: connection)
|
||||||
|
|
||||||
// Then the UI should remain unchanged.
|
// Then the UI should remain unchanged.
|
||||||
app.assertScreenshot(.roomLayoutMiddle, step: 0)
|
app.assertScreenshot(.roomLayoutMiddle, step: 0)
|
||||||
|
|
||||||
// When an incoming message arrives
|
// When an incoming message arrives
|
||||||
try await performOperation(.incomingMessage, using: server)
|
try await performOperation(.incomingMessage, using: connection)
|
||||||
|
|
||||||
// Then the UI should still remain unchanged.
|
// Then the UI should still remain unchanged.
|
||||||
app.assertScreenshot(.roomLayoutMiddle, step: 0)
|
app.assertScreenshot(.roomLayoutMiddle, step: 0)
|
||||||
@@ -114,13 +117,14 @@ class RoomScreenUITests: XCTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func testTimelineLayoutAtTop() async throws {
|
func testTimelineLayoutAtTop() async throws {
|
||||||
let server = try UITestsSignalling.Server()
|
let listener = try UITestsSignalling.Listener()
|
||||||
|
|
||||||
let app = Application.launch()
|
let app = Application.launch()
|
||||||
app.goToScreenWithIdentifier(.roomLayoutTop)
|
app.goToScreenWithIdentifier(.roomLayoutTop)
|
||||||
|
|
||||||
try await server.connect()
|
let connection = try await listener.connection()
|
||||||
defer { server.disconnect() }
|
try await Task.sleep(for: .seconds(1)) // Allow the connection to settle on CI/Intel...
|
||||||
|
defer { connection.disconnect() }
|
||||||
|
|
||||||
// Given a timeline that is scrolled to the top.
|
// Given a timeline that is scrolled to the top.
|
||||||
while !app.staticTexts["Bacon ipsum dolor amet commodo incididunt ribeye dolore cupidatat short ribs."].isHittable {
|
while !app.staticTexts["Bacon ipsum dolor amet commodo incididunt ribeye dolore cupidatat short ribs."].isHittable {
|
||||||
@@ -130,23 +134,24 @@ class RoomScreenUITests: XCTestCase {
|
|||||||
app.assertScreenshot(.roomLayoutTop, insets: cropped) // Assert initial state for comparison.
|
app.assertScreenshot(.roomLayoutTop, insets: cropped) // Assert initial state for comparison.
|
||||||
|
|
||||||
// When a back pagination occurs.
|
// When a back pagination occurs.
|
||||||
try await performOperation(.paginate, using: server)
|
try await performOperation(.paginate, using: connection)
|
||||||
|
|
||||||
// Then the bottom of the timeline should remain unchanged (with new items having been added above).
|
// Then the bottom of the timeline should remain unchanged (with new items having been added above).
|
||||||
app.assertScreenshot(.roomLayoutTop, insets: cropped)
|
app.assertScreenshot(.roomLayoutTop, insets: cropped)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testTimelineLayoutAtBottom() async throws {
|
func testTimelineLayoutAtBottom() async throws {
|
||||||
let server = try UITestsSignalling.Server()
|
let listener = try UITestsSignalling.Listener()
|
||||||
|
|
||||||
let app = Application.launch()
|
let app = Application.launch()
|
||||||
app.goToScreenWithIdentifier(.roomLayoutBottom)
|
app.goToScreenWithIdentifier(.roomLayoutBottom)
|
||||||
|
|
||||||
try await server.connect()
|
let connection = try await listener.connection()
|
||||||
defer { server.disconnect() }
|
try await Task.sleep(for: .seconds(1)) // Allow the connection to settle on CI/Intel...
|
||||||
|
defer { connection.disconnect() }
|
||||||
|
|
||||||
// When an incoming message arrives.
|
// When an incoming message arrives.
|
||||||
try await performOperation(.incomingMessage, using: server)
|
try await performOperation(.incomingMessage, using: connection)
|
||||||
|
|
||||||
// Then the timeline should scroll down to reveal the message.
|
// Then the timeline should scroll down to reveal the message.
|
||||||
app.assertScreenshot(.roomLayoutBottom, step: 0)
|
app.assertScreenshot(.roomLayoutBottom, step: 0)
|
||||||
@@ -160,9 +165,9 @@ class RoomScreenUITests: XCTestCase {
|
|||||||
|
|
||||||
// MARK: - Helper Methods
|
// MARK: - Helper Methods
|
||||||
|
|
||||||
private func performOperation(_ operation: UITestsSignal, using server: UITestsSignalling.Server) async throws {
|
private func performOperation(_ operation: UITestsSignal, using connection: UITestsSignalling.Connection) async throws {
|
||||||
try await server.send(operation)
|
try await connection.send(operation)
|
||||||
guard try await server.receive() == .success else { throw UITestsSignalError.unexpected }
|
guard try await connection.receive() == .success else { throw UITestsSignalError.unexpected }
|
||||||
try await Task.sleep(for: .milliseconds(500)) // Allow the timeline to update
|
try await Task.sleep(for: .milliseconds(500)) // Allow the timeline to update
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user