diff --git a/.github/workflows/cron-checks.yml b/.github/workflows/cron-checks.yml index 2b004b3c2..d884820ec 100644 --- a/.github/workflows/cron-checks.yml +++ b/.github/workflows/cron-checks.yml @@ -79,8 +79,7 @@ jobs: name: Test Data E2E (iOS ${{ matrix.ios }}) path: | fastlane/recordings - fastlane/sinatra_log.txt - fastlane/test_output/report.junit + fastlane/stream-chat-test-mock-server/logs/* fastlane/test_output/logs/*/Diagnostics/**/*.txt fastlane/test_output/logs/*/Diagnostics/simctl_diagnostics/DiagnosticReports/* diff --git a/.github/workflows/smoke-checks.yml b/.github/workflows/smoke-checks.yml index abf4bb5db..d6559e65b 100644 --- a/.github/workflows/smoke-checks.yml +++ b/.github/workflows/smoke-checks.yml @@ -118,8 +118,6 @@ jobs: with: name: Test Data UI path: | - fastlane/recordings - fastlane/sinatra_log.txt fastlane/test_output/snapshots fastlane/test_output/logs/*/Diagnostics/**/*.txt fastlane/test_output/logs/*/Diagnostics/simctl_diagnostics/DiagnosticReports/* @@ -199,8 +197,7 @@ jobs: name: Test Data E2E ${{ matrix.batch }} path: | fastlane/recordings - fastlane/sinatra_log.txt - fastlane/test_output/report.junit + fastlane/stream-chat-test-mock-server/logs/* fastlane/test_output/logs/*/Diagnostics/**/*.txt fastlane/test_output/logs/*/Diagnostics/simctl_diagnostics/DiagnosticReports/* diff --git a/.gitignore b/.gitignore index 68bddbcc6..76e01733b 100644 --- a/.gitignore +++ b/.gitignore @@ -93,6 +93,7 @@ app-thinning.plist *.dmg yeetd-normal.pkg *LinkMap.txt +stream-chat-test-mock-server # VSCode .vscode diff --git a/Gemfile b/Gemfile index 26e5e1e38..5aff4335b 100644 --- a/Gemfile +++ b/Gemfile @@ -21,6 +21,8 @@ group :fastlane_dependencies do end group :sinatra_dependencies do + gem 'eventmachine' + gem 'faye-websocket' gem 'puma' gem 'rackup' end diff --git a/Gemfile.lock b/Gemfile.lock index 5cf7a5f8c..1ed13447c 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -85,6 +85,7 @@ GEM dotenv (2.8.1) drb (2.2.3) emoji_regex (3.2.3) + eventmachine (1.2.7) excon (0.112.0) faraday (1.10.4) faraday-em_http (~> 1.0) @@ -172,6 +173,9 @@ GEM xcsize (= 1.2.0) fastlane-sirp (1.0.0) sysrandom (~> 1.0) + faye-websocket (0.12.0) + eventmachine (>= 0.12.0) + websocket-driver (>= 0.8.0) gh_inspector (1.1.3) git (2.3.3) activesupport (>= 5.0) @@ -352,6 +356,10 @@ GEM concurrent-ruby (~> 1.0) uber (0.1.0) unicode-display_width (2.6.0) + websocket-driver (0.8.0) + base64 + websocket-extensions (>= 0.1.0) + websocket-extensions (0.1.5) word_wrap (1.0.0) xcodeproj (1.27.0) CFPropertyList (>= 2.3.3, < 4.0) @@ -374,6 +382,7 @@ PLATFORMS DEPENDENCIES danger danger-commit_lint + eventmachine fastlane fastlane-plugin-create_xcframework fastlane-plugin-lizard @@ -381,6 +390,7 @@ DEPENDENCIES fastlane-plugin-stream_actions (= 0.3.101) fastlane-plugin-versioning fastlane-plugin-xcsize (= 1.2.0) + faye-websocket json lefthook plist diff --git a/Sources/StreamChatSwiftUI/ChatChannel/MessageList/QuotedMessageView.swift b/Sources/StreamChatSwiftUI/ChatChannel/MessageList/QuotedMessageView.swift index 6c56e8120..7dab57b57 100644 --- a/Sources/StreamChatSwiftUI/ChatChannel/MessageList/QuotedMessageView.swift +++ b/Sources/StreamChatSwiftUI/ChatChannel/MessageList/QuotedMessageView.swift @@ -247,7 +247,8 @@ public struct QuotedMessageContentView: View { } .onDisappear(.cancel) .processors([ImageProcessors.Resize(width: options.attachmentSize.width)]) - .priority(.high) } + .priority(.high) + } } .frame(width: hasVoiceAttachments ? nil : options.attachmentSize.width, height: options.attachmentSize.height) .aspectRatio(1, contentMode: .fill) diff --git a/Sources/StreamChatSwiftUI/ViewFactory/DefaultViewFactory.swift b/Sources/StreamChatSwiftUI/ViewFactory/DefaultViewFactory.swift index 6226dc504..e9a641171 100644 --- a/Sources/StreamChatSwiftUI/ViewFactory/DefaultViewFactory.swift +++ b/Sources/StreamChatSwiftUI/ViewFactory/DefaultViewFactory.swift @@ -835,13 +835,13 @@ extension ViewFactory { } public func makeQuotedMessageContentView( - options: QuotedMessageContentViewOptions - ) -> some View { - QuotedMessageContentView( - factory: self, - options: options - ) - } + options: QuotedMessageContentViewOptions + ) -> some View { + QuotedMessageContentView( + factory: self, + options: options + ) + } public func makeCustomAttachmentQuotedView(options: CustomAttachmentQuotedViewOptions) -> some View { EmptyView() diff --git a/StreamChatSwiftUI.xcodeproj/project.pbxproj b/StreamChatSwiftUI.xcodeproj/project.pbxproj index 9eeda981d..ae77448d4 100644 --- a/StreamChatSwiftUI.xcodeproj/project.pbxproj +++ b/StreamChatSwiftUI.xcodeproj/project.pbxproj @@ -108,7 +108,6 @@ 820A61A029D6D78E002257FB /* QuotedReply_Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 820A619F29D6D78E002257FB /* QuotedReply_Tests.swift */; }; 825AADF4283CCDB000237498 /* ThreadPage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 825AADF3283CCDB000237498 /* ThreadPage.swift */; }; 827352FD290BE91100A87385 /* NotificationsHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8492974827ABDDBF00A8EEB0 /* NotificationsHandler.swift */; }; - 829AB4D228578ACF002DC629 /* StreamTestCase+Tags.swift in Sources */ = {isa = PBXBuildFile; fileRef = 829AB4D128578ACF002DC629 /* StreamTestCase+Tags.swift */; }; 829AB4D42858A532002DC629 /* Reactions_Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 829AB4D32858A532002DC629 /* Reactions_Tests.swift */; }; 829CD5CE2848CA6B003C3877 /* Settings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 829CD5CD2848CA6B003C3877 /* Settings.swift */; }; 829EF8772A9362C00045D166 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 829EF8762A9362C00045D166 /* PrivacyInfo.xcprivacy */; }; @@ -697,7 +696,6 @@ 4FEAB3172BFF71F70057E511 /* SwiftUI+UIAlertController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SwiftUI+UIAlertController.swift"; sourceTree = ""; }; 820A619F29D6D78E002257FB /* QuotedReply_Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QuotedReply_Tests.swift; sourceTree = ""; }; 825AADF3283CCDB000237498 /* ThreadPage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThreadPage.swift; sourceTree = ""; }; - 829AB4D128578ACF002DC629 /* StreamTestCase+Tags.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "StreamTestCase+Tags.swift"; sourceTree = ""; }; 829AB4D32858A532002DC629 /* Reactions_Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Reactions_Tests.swift; sourceTree = ""; }; 829CD5CD2848CA6B003C3877 /* Settings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Settings.swift; sourceTree = ""; }; 829EF8762A9362C00045D166 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = PrivacyInfo.xcprivacy; sourceTree = ""; }; @@ -2240,13 +2238,12 @@ isa = PBXGroup; children = ( A3600B3F283F651200E1C930 /* Base TestCase */, + 82A1814528FD69E8005F9D43 /* Message Delivery Status */, 82A1813D28FD68A3005F9D43 /* ChannelList_Tests.swift */, A3600B31283E9E4700E1C930 /* MessageList_Tests.swift */, 829AB4D32858A532002DC629 /* Reactions_Tests.swift */, 82A1813B28F9BA53005F9D43 /* Attachments_Tests.swift */, 82A1814328FD69AE005F9D43 /* SlowMode_Tests.swift */, - 829AB4D128578ACF002DC629 /* StreamTestCase+Tags.swift */, - 82A1814528FD69E8005F9D43 /* Message Delivery Status */, 82A1813F28FD691B005F9D43 /* Ephemeral_Messages_Tests.swift */, 82A1814128FD694A005F9D43 /* PushNotification_Tests.swift */, 820A619F29D6D78E002257FB /* QuotedReply_Tests.swift */, @@ -2587,7 +2584,6 @@ 82A1814728FD69F8005F9D43 /* MessageDeliveryStatus_Tests.swift in Sources */, A3600B2F283E9E1900E1C930 /* UserRobot+Asserts.swift in Sources */, 82A1814028FD691B005F9D43 /* Ephemeral_Messages_Tests.swift in Sources */, - 829AB4D228578ACF002DC629 /* StreamTestCase+Tags.swift in Sources */, 82A1813C28F9BA53005F9D43 /* Attachments_Tests.swift in Sources */, A3600B35283E9EBA00E1C930 /* Bundle+Target.swift in Sources */, 82A1813A28F84CAA005F9D43 /* SpringBoard.swift in Sources */, @@ -3875,7 +3871,7 @@ isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/GetStream/stream-chat-swift.git"; requirement = { - branch = v5; + branch = "ci/v5-mock-server"; kind = branch; }; }; diff --git a/StreamChatSwiftUITestsAppTests/Pages/ChannelListPage.swift b/StreamChatSwiftUITestsAppTests/Pages/ChannelListPage.swift index 4cf37f9b8..cbdaf8163 100644 --- a/StreamChatSwiftUITestsAppTests/Pages/ChannelListPage.swift +++ b/StreamChatSwiftUITestsAppTests/Pages/ChannelListPage.swift @@ -50,7 +50,10 @@ enum ChannelListPage { cell.staticTexts["UnreadIndicatorView"] } - static func statusCheckmark(for status: MessageDeliveryStatus?, in cell: XCUIElement) -> XCUIElement { + static func statusCheckmark( + for status: StreamChatTestMockServer.MessageDeliveryStatus?, + in cell: XCUIElement + ) -> XCUIElement { return cell.images["readIndicatorCheckmark"] } } diff --git a/StreamChatSwiftUITestsAppTests/Pages/MessageListPage.swift b/StreamChatSwiftUITestsAppTests/Pages/MessageListPage.swift index d46c2d52a..9a82e41b1 100644 --- a/StreamChatSwiftUITestsAppTests/Pages/MessageListPage.swift +++ b/StreamChatSwiftUITestsAppTests/Pages/MessageListPage.swift @@ -115,7 +115,7 @@ class MessageListPage { } static func threadReplyCountButton(in messageCell: XCUIElement) -> XCUIElement { - messageCell.buttons["MessageAvatarViewPlaceholder"] + app.buttons.matching(NSPredicate(format: "identifier LIKE 'MessageAvatarView' or identifier LIKE 'MessageAvatarViewPlaceholder'")).firstMatch } static func reactions(in messageCell: XCUIElement) -> XCUIElementQuery { @@ -167,7 +167,10 @@ class MessageListPage { } // FIXME: - static func statusCheckmark(for status: MessageDeliveryStatus? = nil, in messageCell: XCUIElement) -> XCUIElement { + static func statusCheckmark( + for status: StreamChatTestMockServer.MessageDeliveryStatus? = nil, + in messageCell: XCUIElement + ) -> XCUIElement { messageCell.images["readIndicatorCheckmark"] } @@ -225,7 +228,7 @@ class MessageListPage { } static func files(in messageCell: XCUIElement) -> XCUIElementQuery { - messageCell.buttons.matching(NSPredicate(format: "identifier LIKE 'FileAttachmentsContainer'")) + messageCell.buttons.matching(NSPredicate(format: "identifier LIKE 'FileAttachmentsContainer'")).images } static func videoPlayer() -> XCUIElement { diff --git a/StreamChatSwiftUITestsAppTests/Pages/SpringBoard.swift b/StreamChatSwiftUITestsAppTests/Pages/SpringBoard.swift index e94aecf42..496f491c2 100644 --- a/StreamChatSwiftUITestsAppTests/Pages/SpringBoard.swift +++ b/StreamChatSwiftUITestsAppTests/Pages/SpringBoard.swift @@ -22,4 +22,10 @@ enum SpringBoard { static var testAppIcon: XCUIElement { app.icons["Chat UI Tests"] } + + static var photoAccessPopUp: XCUIElement { + app.buttons + .matching(NSPredicate(format: "label LIKE 'Allow Full Access'")) + .firstMatch + } } diff --git a/StreamChatSwiftUITestsAppTests/Pages/ThreadPage.swift b/StreamChatSwiftUITestsAppTests/Pages/ThreadPage.swift index eca7e35b9..1c09cacd0 100644 --- a/StreamChatSwiftUITestsAppTests/Pages/ThreadPage.swift +++ b/StreamChatSwiftUITestsAppTests/Pages/ThreadPage.swift @@ -7,4 +7,5 @@ import XCTest class ThreadPage: MessageListPage { static var alsoSendInChannelCheckbox: XCUIElement { app.buttons["SendInChannelView"] } + static var repliesCountLabel: XCUIElement { app.staticTexts["textLabel"] } } diff --git a/StreamChatSwiftUITestsAppTests/Robots/UserRobot+Asserts.swift b/StreamChatSwiftUITestsAppTests/Robots/UserRobot+Asserts.swift index cbd910dee..fa4218b1a 100644 --- a/StreamChatSwiftUITestsAppTests/Robots/UserRobot+Asserts.swift +++ b/StreamChatSwiftUITestsAppTests/Robots/UserRobot+Asserts.swift @@ -3,7 +3,6 @@ // import Foundation -import StreamChat @testable import StreamChatSwiftUI import XCTest @@ -36,6 +35,25 @@ extension UserRobot { ) return channelCells.element(boundBy: index) } + + @discardableResult + func assertChannelPreviewIsEmpty( + at cellIndex: Int? = nil, + file: StaticString = #filePath, + line: UInt = #line + ) -> Self { + let emptyChannelPreviewText = "No messages" + let cell = channelCell(withIndex: cellIndex, file: file, line: line) + let message = channelAttributes.lastMessage(in: cell) + let actualText = message.waitForText(emptyChannelPreviewText, mustBeEqual: true).text + XCTAssertEqual( + actualText, + emptyChannelPreviewText, + file: file, + line: line + ) + return self + } @discardableResult func assertLastMessageInChannelPreview( @@ -76,7 +94,7 @@ extension UserRobot { @discardableResult func assertMessageDeliveryStatusInChannelPreview( - _ deliveryStatus: MessageDeliveryStatus?, + _ deliveryStatus: StreamChatTestMockServer.MessageDeliveryStatus?, at cellIndex: Int? = nil, file: StaticString = #filePath, line: UInt = #line @@ -164,17 +182,19 @@ extension UserRobot { line: UInt = #line ) -> XCUIElement { let messageCell: XCUIElement - if let index = index { - let minExpectedCount = index + 1 + let messageIndex = index != nil && index! > 25 ? cells.count - 1 : index + + if let messageIndex = messageIndex { + let minExpectedCount = messageIndex + 1 let cells = cells.waitCount(minExpectedCount) XCTAssertGreaterThanOrEqual( cells.count, minExpectedCount, - "Message cell is not found at index #\(index)", + "Message cell is not found at index #\(messageIndex)", file: file, line: line ) - messageCell = cells.element(boundBy: index) + messageCell = cells.element(boundBy: messageIndex) } else { messageCell = cells.firstMatch } @@ -195,10 +215,31 @@ extension UserRobot { return self } + @discardableResult + func assertOldestLoadedMessage( + isEqual: Bool, + to text: String, + file: StaticString = #filePath, + line: UInt = #line + ) -> Self { + if let topMessageCell = cells.lastMatch { + let message = attributes.text(in: topMessageCell).wait() + let actualText = message.text + if isEqual { + XCTAssertEqual(text, actualText, file: file, line: line) + } else { + XCTAssertNotEqual(text, actualText, file: file, line: line) + } + } else { + XCTFail("lastMessageCell cannot be found") + } + return self + } + @discardableResult func assertPushNotification( - withText text: String, - from sender: String, + title: String, + body: String, file: StaticString = #filePath, line: UInt = #line ) -> Self { @@ -212,14 +253,14 @@ extension UserRobot { let pushNotificationContent = pushNotification.text XCTAssertTrue( - pushNotificationContent.contains(text), - "\(pushNotificationContent) does not contain \(text)", + pushNotificationContent.contains(body), + "\(pushNotificationContent) does not contain \(body)", file: file, line: line ) XCTAssertTrue( - pushNotificationContent.contains(sender), - "\(pushNotificationContent) does not contain \(sender)", + pushNotificationContent.contains(title), + "\(pushNotificationContent) does not contain \(title)", file: file, line: line ) @@ -294,6 +335,19 @@ extension UserRobot { XCTAssertEqual(text, actualText, file: file, line: line) return self } + + @discardableResult + func assertFirstMessageIsVisible( + _ text: String, + file: StaticString = #filePath, + line: UInt = #line + ) -> Self { + let messageCell = messageCell(withIndex: cells.count - 1, file: file, line: line) + let message = attributes.text(in: messageCell).wait() + let actualText = message.waitForText(text).text + XCTAssertEqual(text, actualText, file: file, line: line) + return self + } @discardableResult func assertMessageIsNotVisible( @@ -381,11 +435,12 @@ extension UserRobot { @discardableResult func assertScrollToBottomButton( isVisible: Bool, + timeout: Double = XCUIElement.waitTimeout, file: StaticString = #filePath, line: UInt = #line ) -> Self { var btn = MessageListPage.scrollToBottomButton - btn = isVisible ? btn.wait() : btn.waitForDisappearance() + btn = isVisible ? btn.wait() : btn.waitForDisappearance(timeout: timeout) XCTAssertEqual( isVisible, btn.exists, @@ -474,7 +529,7 @@ extension UserRobot { @discardableResult func waitForMessageDeliveryStatus( - _ deliveryStatus: MessageDeliveryStatus?, + _ deliveryStatus: StreamChatTestMockServer.MessageDeliveryStatus?, at messageCellIndex: Int? = nil, file: StaticString = #filePath, line: UInt = #line @@ -490,7 +545,7 @@ extension UserRobot { @discardableResult func assertMessageDeliveryStatus( - _ deliveryStatus: MessageDeliveryStatus?, + _ deliveryStatus: StreamChatTestMockServer.MessageDeliveryStatus?, at messageCellIndex: Int? = nil, file: StaticString = #filePath, line: UInt = #line @@ -529,7 +584,6 @@ extension UserRobot { let obtainKeyboardFocus = (i == 1) ? true : false typeText("\(i)\n", obtainKeyboardFocus: obtainKeyboardFocus) let updatedComposerHeight = composer.height - print(updatedComposerHeight) XCTAssertGreaterThan(updatedComposerHeight, composerHeight, file: file, line: line) composerHeight = updatedComposerHeight } @@ -582,7 +636,7 @@ extension UserRobot { file: StaticString = #filePath, line: UInt = #line ) -> Self { - let endTime = Date().timeIntervalSince1970 * 1000 + XCUIElement.waitTimeout * 2000 + let endTime = Date().timeIntervalSince1970 * 1000 + XCUIElement.waitTimeout * 2500 let actualCount = MessageListPage.cells.count XCTAssertNotEqual(expectedCount, actualCount, file: file, line: line) @@ -655,8 +709,8 @@ extension UserRobot { } @discardableResult - func assertMentionWasApplied(file: StaticString = #filePath, line: UInt = #line) -> Self { - let expectedText = "@\(UserDetails.countDookuName)" + func assertMentionWasApplied(userName: String, file: StaticString = #filePath, line: UInt = #line) -> Self { + let expectedText = "@\(userName)" let actualText = MessageListPage.Composer.textView.waitForText(expectedText).text XCTAssertEqual(expectedText, actualText, file: file, line: line) return self @@ -793,7 +847,7 @@ extension UserRobot { @discardableResult func assertThreadReplyDeliveryStatus( - _ deliveryStatus: MessageDeliveryStatus?, + _ deliveryStatus: StreamChatTestMockServer.MessageDeliveryStatus?, at messageCellIndex: Int? = nil, file: StaticString = #filePath, line: UInt = #line @@ -813,33 +867,24 @@ extension UserRobot { } @discardableResult - func assertCooldownIsShown(file: StaticString = #filePath, line: UInt = #line) -> Self { + func assertCooldown(shouldBeVisible: Bool, file: StaticString = #filePath, line: UInt = #line) -> Self { + if shouldBeVisible { + MessageListPage.Composer.cooldown.wait() + } else { + MessageListPage.Composer.cooldown.waitForDisappearance() + } + XCTAssertEqual( - MessageListPage.Composer.placeholder.text, - L10n.Composer.Placeholder.slowMode, - file: file, - line: line - ) - XCTAssertTrue( - MessageListPage.Composer.cooldown.wait().exists, + MessageListPage.Composer.cooldown.exists, + shouldBeVisible, "Cooldown should be visible", file: file, line: line ) - return self - } - - @discardableResult - func assertCooldownIsNotShown(file: StaticString = #filePath, line: UInt = #line) -> Self { - XCTAssertNotEqual( - MessageListPage.Composer.placeholder.text, - L10n.Composer.Placeholder.slowMode, - file: file, - line: line - ) - XCTAssertFalse( - MessageListPage.Composer.cooldown.exists, - "Cooldown should not be visible", + + XCTAssertEqual( + MessageListPage.Composer.placeholder.text == L10n.Composer.Placeholder.slowMode, + shouldBeVisible, file: file, line: line ) @@ -852,10 +897,11 @@ extension UserRobot { XCTAssertFalse(sendButton.exists, "Send button is visible", file: file, line: line) return self } - + @discardableResult func assertThreadReplyCountButton( at messageCellIndex: Int? = nil, + replies: Int, file: StaticString = #filePath, line: UInt = #line ) -> Self { @@ -867,6 +913,32 @@ extension UserRobot { file: file, line: line ) + + let expectedText = replies == 1 ? "1 Thread Reply" : "\(replies) Thread Replies" + XCTAssertEqual(expectedText, threadReplyCountButton.waitForText(expectedText).text) + return self + } + + @discardableResult + func assertThreadRepliesCountLabel( + _ count: Int, + file: StaticString = #filePath, + line: UInt = #line + ) -> Self { + let expectedLabel = "\(count) REPLIES" + let repliesCountLabel = ThreadPage.repliesCountLabel.waitForText(expectedLabel).text + XCTAssertEqual(repliesCountLabel, repliesCountLabel, file: file, line: line) + return self + } + + @discardableResult + func assertParentMessageInThread( + withText text: String, + isLoaded: Bool, + file: StaticString = #filePath, + line: UInt = #line + ) -> Self { + assertOldestLoadedMessage(isEqual: isLoaded, to: text) return self } } @@ -939,7 +1011,7 @@ extension UserRobot { line: UInt = #line ) -> Self { let cell = messageCell(withIndex: messageCellIndex, file: file, line: line).wait() - let expectedText = Message.message(withInvalidCommand: invalidCommand) + let expectedText = "Sorry, command \(invalidCommand) doesn't exist. Try posting your message without the starting /" let actualText = attributes.systemMessage(in: cell).waitForText(expectedText).text XCTAssertEqual(actualText, expectedText, file: file, line: line) return self @@ -1022,7 +1094,7 @@ extension UserRobot { ) -> Self { let messageCell = messageCell(withIndex: messageCellIndex, file: file, line: line) let files = attributes.files(in: messageCell) - let errMessage = isPresent ? "There are no files" : "Files are presented" + let errMessage = isPresent ? "There are wrong number of files" : "Files should not be present" _ = isPresent ? files.firstMatch.wait() : files.firstMatch.waitForDisappearance() XCTAssertEqual(files.count, count, errMessage, file: file, line: line) return self diff --git a/StreamChatSwiftUITestsAppTests/Robots/UserRobot.swift b/StreamChatSwiftUITestsAppTests/Robots/UserRobot.swift index f0afd185e..0b431ca18 100644 --- a/StreamChatSwiftUITestsAppTests/Robots/UserRobot.swift +++ b/StreamChatSwiftUITestsAppTests/Robots/UserRobot.swift @@ -38,8 +38,6 @@ final class UserRobot: Robot { if !cells.firstMatch.exists { for _ in 0...10 { app.terminate() - server.stop() - _ = server.start(port: UInt16(MockServerConfiguration.port)) sleep(1) app.launch() login() @@ -58,6 +56,13 @@ final class UserRobot: Robot { ChannelListPage.cells.allElementsBoundByIndex[channelCellIndex].waitForHitPoint().safeTap() return self } + + @discardableResult + public func sleep(_ seconds: Double) -> Self { + let sleepTime = UInt32(seconds * 1_000_000) + usleep(sleepTime) + return self + } } // MARK: Message List @@ -87,15 +92,10 @@ extension UserRobot { file: StaticString = #filePath, line: UInt = #line ) -> Self { - server.channelsEndpointWasCalled = false - typeText(text) composer.sendButton.safeTap() if waitForAppearance { - server.waitForWebsocketMessage(withText: text) - server.waitForHttpMessage(withText: text) - let cell = messageCell(withIndex: messageCellIndex, file: file, line: line).wait() let textView = attributes.text(in: cell) _ = textView.waitForText(text) @@ -143,7 +143,7 @@ extension UserRobot { @discardableResult private func reactionAction( - reactionType: TestData.Reactions, + reactionType: ReactionType, eventType: EventType, messageCellIndex: Int ) -> Self { @@ -169,7 +169,7 @@ extension UserRobot { } @discardableResult - func addReaction(type: TestData.Reactions, messageCellIndex: Int = 0) -> Self { + func addReaction(type: ReactionType, messageCellIndex: Int = 0) -> Self { reactionAction( reactionType: type, eventType: .reactionNew, @@ -178,7 +178,7 @@ extension UserRobot { } @discardableResult - func deleteReaction(type: TestData.Reactions, messageCellIndex: Int = 0) -> Self { + func deleteReaction(type: ReactionType, messageCellIndex: Int = 0) -> Self { reactionAction( reactionType: type, eventType: .reactionDeleted, @@ -204,7 +204,6 @@ extension UserRobot { selectOptionFromContextMenu(option: .reply, forMessageAtIndex: messageCellIndex) sendMessage( text, - at: messageCellIndex, waitForAppearance: waitForAppearance, file: file, line: line @@ -282,7 +281,7 @@ extension UserRobot { } @discardableResult - func replyToMessageInThread( + func sendMessageInThread( _ text: String, alsoSendInChannel: Bool = false, messageCellIndex: Int = 0, @@ -349,6 +348,13 @@ extension UserRobot { } return self } + + @discardableResult + func swipeMessage(at index: Int = 0) -> Self { + let cell = messageCell(withIndex: index).waitForHitPoint() + cell.staticTexts.firstMatch.swipeRight() + return self + } @discardableResult func openComposerCommands() -> Self { @@ -360,7 +366,7 @@ extension UserRobot { } @discardableResult - func sendGiphy(text: String = "Test", useComposerCommand: Bool = false, send: Bool = true) -> Self { + func uploadGiphy(text: String = "Test", useComposerCommand: Bool = false, send: Bool = true) -> Self { if useComposerCommand { openComposerCommands() MessageListPage.ComposerCommands.giphyImage.wait().safeTap() @@ -378,7 +384,7 @@ extension UserRobot { func replyWithGiphy(useComposerCommand: Bool = false, messageCellIndex: Int = 0) -> Self { return self .selectOptionFromContextMenu(option: .reply, forMessageAtIndex: messageCellIndex) - .sendGiphy(useComposerCommand: useComposerCommand) + .uploadGiphy(useComposerCommand: useComposerCommand) } @discardableResult @@ -394,7 +400,7 @@ extension UserRobot { if alsoSendInChannel { threadCheckbox.wait().safeTap() } - return sendGiphy(useComposerCommand: useComposerCommand) + return uploadGiphy(useComposerCommand: useComposerCommand) } @discardableResult @@ -406,8 +412,12 @@ extension UserRobot { @discardableResult func uploadImage(count: Int = 1, send: Bool = true) -> Self { - for i in 1...count { + let firstImageIndex = 1 + for i in firstImageIndex...count { MessageListPage.Composer.attachmentButton.wait().safeTap() + if i == firstImageIndex && SpringBoard.photoAccessPopUp.exists { + SpringBoard.photoAccessPopUp.safeTap() + } MessageListPage.AttachmentMenu.photoOrVideoButton.wait().safeTap() // Wait for privacy message to appear before proceed on iOS 17, otherwise XCTest crashes @@ -422,8 +432,8 @@ extension UserRobot { } @discardableResult - func mentionParticipant(manually: Bool = false) -> Self { - let text = "@\(UserDetails.countDookuId)" + func mentionParticipant(_ userId: String, manually: Bool = false) -> Self { + let text = "@\(userId)" if manually { typeText(text) } else { @@ -444,7 +454,7 @@ extension UserRobot { } @discardableResult - func addParticipant(withUserId userId: String = UserDetails.leiaOrganaId) -> Self { + func addParticipant(withUserId userId: String = "leia_organa") -> Self { tapOnDebugMenu() debugAlert.addMember.firstMatch.safeTap() debugAlert.addMemberTextField.firstMatch @@ -455,7 +465,7 @@ extension UserRobot { } @discardableResult - func removeParticipant(withUserId userId: String = UserDetails.leiaOrganaId) -> Self { + func removeParticipant(withUserId userId: String = "leia_organa") -> Self { tapOnDebugMenu() debugAlert.removeMember.firstMatch.safeTap() debugAlert.selectMember(withUserId: userId).firstMatch.safeTap() diff --git a/StreamChatSwiftUITestsAppTests/Tests/Attachments_Tests.swift b/StreamChatSwiftUITestsAppTests/Tests/Attachments_Tests.swift index 05fd667a9..bb9087739 100644 --- a/StreamChatSwiftUITestsAppTests/Tests/Attachments_Tests.swift +++ b/StreamChatSwiftUITestsAppTests/Tests/Attachments_Tests.swift @@ -4,24 +4,8 @@ import XCTest +// NOTE: Attachments tests used to freeze the test app on iOS > 18" final class Attachments_Tests: StreamTestCase { - override func setUpWithError() throws { - try XCTSkipIf( - ProcessInfo().operatingSystemVersion.majorVersion >= 18, - "Attachments tests freeze the test app on iOS > 18" - ) - - try super.setUpWithError() - addTags([.coreFeatures]) - assertMockServer() - } - - override func tearDownWithError() throws { - if ProcessInfo().operatingSystemVersion.majorVersion < 18 { - try super.tearDownWithError() - } - } - func test_uploadImage() throws { linkToScenario(withId: 310) @@ -73,10 +57,35 @@ final class Attachments_Tests: StreamTestCase { userRobot.login().openChannel() } WHEN("participant uploads a file") { - participantRobot.uploadAttachment(type: .file, waitBeforeSending: 2) + participantRobot.uploadAttachment(type: .file) } THEN("user can see uploaded file") { userRobot.assertFile(isPresent: true) } } + + func test_restartImageUpload() throws { + linkToScenario(withId: 9821) + + throw XCTSkip("https://linear.app/stream/issue/IOS-1315") + + GIVEN("user opens the channel") { + userRobot + .login() + .openChannel() + } + WHEN("user sends an image beeing offline") { + userRobot + .setConnectivity(to: .off) + .uploadImage() + } + AND("user restarts an image upload being online") { + userRobot + .setConnectivity(to: .on) +// .restartImageUpload() + } + THEN("user can see uploaded image") { + userRobot.assertImage(isPresent: true) + } + } } diff --git a/StreamChatSwiftUITestsAppTests/Tests/Base TestCase/StreamTestCase.swift b/StreamChatSwiftUITestsAppTests/Tests/Base TestCase/StreamTestCase.swift index 17a77ff5a..c2c833451 100644 --- a/StreamChatSwiftUITestsAppTests/Tests/Base TestCase/StreamTestCase.swift +++ b/StreamChatSwiftUITestsAppTests/Tests/Base TestCase/StreamTestCase.swift @@ -4,7 +4,6 @@ import XCTest -// Application let app = XCUIApplication() class StreamTestCase: XCTestCase { @@ -12,30 +11,27 @@ class StreamTestCase: XCTestCase { var userRobot: UserRobot! var backendRobot: BackendRobot! var participantRobot: ParticipantRobot! - var server: StreamMockServer! - var mockServerCrashed = false - var recordVideo = false + var mockServer: StreamMockServer! + var useMockServer = true + var switchApiKey: String? override func setUpWithError() throws { continueAfterFailure = false - startMockServer() - participantRobot = ParticipantRobot(server) - backendRobot = BackendRobot(server) - userRobot = UserRobot(server) try super.setUpWithError() + mockServer = StreamMockServer(driverPort: "4568", testName: testName) + backendRobot = BackendRobot(mockServer) + participantRobot = ParticipantRobot(mockServer) + userRobot = UserRobot(mockServer) alertHandler() - useMockServer() - startVideo() + backendHandler() app.launch() } override func tearDownWithError() throws { attachElementTree() - stopVideo() app.terminate() - server.stop() - backendRobot.delayServerResponse(byTimeInterval: 0.0) + mockServer.stop() try super.tearDownWithError() app.launchArguments.removeAll() @@ -44,20 +40,18 @@ class StreamTestCase: XCTestCase { } extension StreamTestCase { - func assertMockServer() { - XCTAssertFalse(mockServerCrashed, "Mock server failed on start") - } - - private func useMockServer() { - // Leverage web socket server - app.setLaunchArguments(.useMockServer) - - // Configure web socket host + private func backendHandler() { app.setEnvironmentVariables([ - .websocketHost: "\(MockServerConfiguration.websocketHost)", - .httpHost: "\(MockServerConfiguration.httpHost)", - .port: "\(MockServerConfiguration.port)" + .websocketHost: "ws://localhost", + .httpHost: "http://localhost", + .port: StreamMockServer.port! ]) + + if useMockServer { + app.setLaunchArguments(.useMockServer) + } else if let switchApiKey { + app.setEnvironmentVariables([.customApiKey: switchApiKey]) + } } private func attachElementTree() { @@ -67,9 +61,9 @@ extension StreamTestCase { } private func alertHandler() { - let title = "Notification Alert" + let title = "Push Notification Alert" _ = addUIInterruptionMonitor(withDescription: title) { (alert: XCUIElement) -> Bool in - let allowButton = alert.buttons.matching(NSPredicate(format: "label LIKE 'Allow' or label LIKE 'Allow Full Access' or label LIKE 'Allow Access to All Photos'")).firstMatch + let allowButton = alert.buttons["Allow"] if allowButton.exists { allowButton.tap() return true @@ -78,34 +72,6 @@ extension StreamTestCase { } } - private func startMockServer() { - server = StreamMockServer() - server.configure() - - for _ in 0...3 { - let serverHasStarted = server.start(port: UInt16(MockServerConfiguration.port)) - if serverHasStarted { - return - } - server.stop() - MockServerConfiguration.port = UInt16.random(in: 61000..<62000) - } - - mockServerCrashed = true - } - - private func startVideo() { - if recordVideo { - server.recordVideo(name: testName) - } - } - - private func stopVideo() { - if recordVideo { - server.recordVideo(name: testName, delete: !isTestFailed(), stop: true) - } - } - private func isTestFailed() -> Bool { if let testRun = testRun { let failureCount = testRun.failureCount + testRun.unexpectedExceptionCount diff --git a/StreamChatSwiftUITestsAppTests/Tests/ChannelList_Tests.swift b/StreamChatSwiftUITestsAppTests/Tests/ChannelList_Tests.swift index 0f2bae593..697492c96 100644 --- a/StreamChatSwiftUITestsAppTests/Tests/ChannelList_Tests.swift +++ b/StreamChatSwiftUITestsAppTests/Tests/ChannelList_Tests.swift @@ -7,12 +7,6 @@ import XCTest final class ChannelList_Tests: StreamTestCase { let message = "message" - override func setUpWithError() throws { - try super.setUpWithError() - addTags([.coreFeatures]) - assertMockServer() - } - func test_newMessageShownInChannelPreview_whenComingBackFromChannel() { linkToScenario(withId: 348) @@ -34,8 +28,8 @@ final class ChannelList_Tests: StreamTestCase { func test_participantMessageShownInChannelPreview_whenReturningFromOffline() throws { linkToScenario(withId: 349) - - throw XCTSkip("Check out SWUI-245") + + throw XCTSkip("https://linear.app/stream/issue/IOS-1315") GIVEN("user opens the channel") { userRobot @@ -50,7 +44,7 @@ final class ChannelList_Tests: StreamTestCase { WHEN("participant sends a new message") { participantRobot .sendMessage(message) - .wait(2.0) + .sleep(2.0) } AND("user becomes online") { userRobot.setConnectivity(to: .on) @@ -68,7 +62,7 @@ final class ChannelList_Tests: StreamTestCase { let channelsCount = 30 WHEN("user opens the channel list") { - backendRobot.generateChannels(count: channelsCount) + backendRobot.generateChannels(channelsCount: channelsCount) userRobot.login() } THEN("user makes sure that all channels are loaded") { @@ -124,8 +118,8 @@ extension ChannelList_Tests { func test_channelPreviewShowsNoMessages_whenTheOnlyMessageInChannelIsDeleted() throws { linkToScenario(withId: 353) - - throw XCTSkip("Check out SWUI-246") + + throw XCTSkip("https://linear.app/stream/issue/IOS-1317") let message = "Hey" @@ -154,8 +148,6 @@ extension ChannelList_Tests { func test_channelPreviewShowsPreviousMessage_whenLastMessageIsDeleted() throws { linkToScenario(withId: 354) - throw XCTSkip("Check out SWUI-246") - let message1 = "Previous message" let message2 = "Last message" @@ -186,8 +178,6 @@ extension ChannelList_Tests { func test_channelPreviewIsNotUpdated_whenThreadReplyIsSent() throws { linkToScenario(withId: 355) - throw XCTSkip("Check out SWUI-244") - let channelMessage = "Channel message" let threadReply = "Thread reply" @@ -200,7 +190,7 @@ extension ChannelList_Tests { userRobot.sendMessage(channelMessage) } AND("user adds thread reply to this message") { - userRobot.replyToMessageInThread(threadReply) + userRobot.sendMessageInThread(threadReply) } WHEN("user goes back to the channel list") { userRobot.moveToChannelListFromThreadReplies() @@ -241,39 +231,3 @@ extension ChannelList_Tests { } } } - -// MARK: - Truncate channel - -extension ChannelList_Tests { - func test_messageList_and_channelPreview_AreUpdatedWhenChannelTruncatedWithMessage() throws { - linkToScenario(withId: 357) - - throw XCTSkip("Check out SWUI-245") - - let message = "Channel truncated" - - GIVEN("user opens the channel") { - backendRobot.generateChannels(count: 1, messagesCount: 42) - userRobot.login().openChannel() - } - WHEN("user truncates the channel with system message") { - userRobot.truncateChannel(withMessage: true) - } - THEN("user observes only the system message") { - userRobot - .assertMessage(message) - .assertMessageCount(1) - .assertScrollToBottomButton(isVisible: false) - .assertScrollToBottomButtonUnreadCount(0) - } - WHEN("user goes to channel list") { - userRobot.tapOnBackButton() - } - THEN("the channel preview shows system message") { - userRobot.assertLastMessageInChannelPreview(message) - } - AND("last message timestamp is shown") { - userRobot.assertLastMessageTimestampInChannelPreview(isHidden: false) - } - } -} diff --git a/StreamChatSwiftUITestsAppTests/Tests/Ephemeral_Messages_Tests.swift b/StreamChatSwiftUITestsAppTests/Tests/Ephemeral_Messages_Tests.swift index aa74183b7..bb68774af 100644 --- a/StreamChatSwiftUITestsAppTests/Tests/Ephemeral_Messages_Tests.swift +++ b/StreamChatSwiftUITestsAppTests/Tests/Ephemeral_Messages_Tests.swift @@ -5,11 +5,6 @@ import XCTest final class Ephemeral_Messages_Tests: StreamTestCase { - override func setUpWithError() throws { - try super.setUpWithError() - assertMockServer() - } - func test_userObservesAnimatedGiphy_whenUserAddsGiphyMessage() throws { linkToScenario(withId: 435) @@ -19,7 +14,7 @@ final class Ephemeral_Messages_Tests: StreamTestCase { .openChannel() } WHEN("user sends a giphy using giphy command") { - userRobot.sendGiphy(useComposerCommand: true) + userRobot.uploadGiphy(useComposerCommand: true) } THEN("user observes the animated gif") { userRobot.assertGiphyImage() @@ -35,7 +30,7 @@ final class Ephemeral_Messages_Tests: StreamTestCase { .openChannel() } WHEN("participant sends a giphy") { - participantRobot.sendGiphy() + participantRobot.uploadGiphy() } THEN("user observes the animated gif") { userRobot.assertGiphyImage() @@ -74,15 +69,13 @@ final class Ephemeral_Messages_Tests: StreamTestCase { func test_channelListNotModified_whenEphemeralMessageShown() throws { linkToScenario(withId: 438) - throw XCTSkip("Check out SWUI-252") - GIVEN("user opens a channel") { userRobot .login() .openChannel() } WHEN("user runs a giphy command") { - userRobot.sendGiphy(send: false) + userRobot.uploadGiphy(send: false) } WHEN("user goes back to channel list") { userRobot.tapOnBackButton() @@ -94,8 +87,8 @@ final class Ephemeral_Messages_Tests: StreamTestCase { func test_deliveryStatusHidden_whenEphemeralMessageShown() throws { linkToScenario(withId: 439) - - throw XCTSkip("Check out SWUI-245") + + throw XCTSkip("https://linear.app/stream/issue/IOS-1324") GIVEN("user opens a channel") { userRobot @@ -103,7 +96,7 @@ final class Ephemeral_Messages_Tests: StreamTestCase { .openChannel() } WHEN("user runs a giphy command") { - userRobot.sendGiphy(send: false) + userRobot.uploadGiphy(send: false) } THEN("delivery status is hidden for ephemeral messages") { userRobot @@ -114,17 +107,17 @@ final class Ephemeral_Messages_Tests: StreamTestCase { func test_deliveryStatusHidden_whenEphemeralMessageShownInThread() throws { linkToScenario(withId: 440) - - throw XCTSkip("Check out SWUI-245") + + throw XCTSkip("https://linear.app/stream/issue/IOS-1324") GIVEN("user opens a channel") { - backendRobot.generateChannels(count: 1, messagesCount: 1) + backendRobot.generateChannels(channelsCount: 1, messagesCount: 1) userRobot.login().openChannel() } WHEN("user runs a giphy command in thread") { userRobot .openThread() - .sendGiphy(send: false) + .uploadGiphy(send: false) } THEN("delivery status is hidden for ephemeral messages") { userRobot @@ -142,7 +135,7 @@ final class Ephemeral_Messages_Tests: StreamTestCase { .openChannel() } WHEN("user sends a giphy using giphy command") { - userRobot.sendGiphy(useComposerCommand: true) + userRobot.uploadGiphy(useComposerCommand: true) } THEN("user observes the animated gif") { userRobot.assertGiphyImage() diff --git a/StreamChatSwiftUITestsAppTests/Tests/Message Delivery Status/MessageDeliveryStatus+ChannelList_Tests.swift b/StreamChatSwiftUITestsAppTests/Tests/Message Delivery Status/MessageDeliveryStatus+ChannelList_Tests.swift index 121936b18..f35a21819 100644 --- a/StreamChatSwiftUITestsAppTests/Tests/Message Delivery Status/MessageDeliveryStatus+ChannelList_Tests.swift +++ b/StreamChatSwiftUITestsAppTests/Tests/Message Delivery Status/MessageDeliveryStatus+ChannelList_Tests.swift @@ -12,24 +12,16 @@ final class MessageDeliveryStatus_ChannelList_Tests: StreamTestCase { var pendingThreadReply: String { "pending \(threadReply)" } var failedThreadReply: String { "failed \(threadReply)" } - override func setUpWithError() throws { - try super.setUpWithError() - addTags([.messageDeliveryStatus]) - assertMockServer() - } - func test_deliveryStatusClocksShownInPreview_whenTheLastMessageIsInPendingState() throws { linkToScenario(withId: 424) - throw XCTSkip("Check out SWUI-245") - GIVEN("user opens the channel") { userRobot .login() .openChannel() - backendRobot.delayServerResponse(byTimeInterval: 10.0) } AND("user sends new message") { + backendRobot.delayNewMessages(by: 10) userRobot.sendMessage(message, waitForAppearance: false) } WHEN("user retuns to the channel list before the message is sent") { @@ -45,8 +37,6 @@ final class MessageDeliveryStatus_ChannelList_Tests: StreamTestCase { func test_singleCheckmarkShownInPreview_whenTheLastMessageIsSent() throws { linkToScenario(withId: 425) - throw XCTSkip("Check out SWUI-245") - GIVEN("user opens the channel") { userRobot .login() @@ -67,8 +57,8 @@ final class MessageDeliveryStatus_ChannelList_Tests: StreamTestCase { func test_errorIndicatorShownInPreview_whenMessageFailedToBeSent() throws { linkToScenario(withId: 426) - - throw XCTSkip("Check out SWUI-245") + + throw XCTSkip("https://linear.app/stream/issue/IOS-1315") GIVEN("user opens the channel") { userRobot @@ -95,8 +85,6 @@ final class MessageDeliveryStatus_ChannelList_Tests: StreamTestCase { func test_doubleCheckmarkShownInPreview_whenMessageReadByParticipant() throws { linkToScenario(withId: 427) - throw XCTSkip("Check out SWUI-245") - GIVEN("user opens the channel") { userRobot .login() @@ -109,7 +97,7 @@ final class MessageDeliveryStatus_ChannelList_Tests: StreamTestCase { userRobot.tapOnBackButton() } WHEN("participant reads the user's message") { - participantRobot.readMessageAfterDelay() + participantRobot.readMessage() } THEN("user spots double checkmark next to the message") { userRobot.assertMessageDeliveryStatusInChannelPreview(.read) @@ -122,8 +110,6 @@ final class MessageDeliveryStatus_ChannelList_Tests: StreamTestCase { func test_deliveryStatusHiddenInPreview_whenMessageIsSentAndReadEventsIsDisabled() throws { linkToScenario(withId: 428) - throw XCTSkip("Check out SWUI-245") - GIVEN("user opens chat") { backendRobot.setReadEvents(to: false) userRobot @@ -146,8 +132,6 @@ final class MessageDeliveryStatus_ChannelList_Tests: StreamTestCase { func test_deliveryStatusHiddenInPreview_whenMessageIsSentByParticipant() throws { linkToScenario(withId: 429) - throw XCTSkip("Check out SWUI-245") - GIVEN("user opens chat") { userRobot .login() @@ -173,8 +157,6 @@ extension MessageDeliveryStatus_ChannelList_Tests { func test_noCheckmarkShownForMessageInPreview_whenThreadReplyIsSent() throws { linkToScenario(withId: 430) - throw XCTSkip("Check out SWUI-245") - GIVEN("user opens chat") { userRobot .login() @@ -184,7 +166,7 @@ extension MessageDeliveryStatus_ChannelList_Tests { participantRobot.sendMessage(message) } AND("user replies to the message in thread") { - userRobot.replyToMessageInThread(threadReply) + userRobot.sendMessageInThread(threadReply) } WHEN("user retuns to the channel list") { userRobot.moveToChannelListFromThreadReplies() @@ -199,8 +181,8 @@ extension MessageDeliveryStatus_ChannelList_Tests { func test_singleCheckmarkShownForMessageInPreview_whenThreadReplyFailedToBeSent() throws { linkToScenario(withId: 431) - - throw XCTSkip("Check out SWUI-245") + + throw XCTSkip("https://linear.app/stream/issue/IOS-1315") GIVEN("user opens the channel") { userRobot @@ -215,7 +197,7 @@ extension MessageDeliveryStatus_ChannelList_Tests { userRobot.setConnectivity(to: .off) } AND("user replies to message in thread") { - userRobot.replyToMessageInThread(failedThreadReply, waitForAppearance: false) + userRobot.sendMessageInThread(failedThreadReply, waitForAppearance: false) } WHEN("user retuns to the channel list") { userRobot.moveToChannelListFromThreadReplies() @@ -231,8 +213,6 @@ extension MessageDeliveryStatus_ChannelList_Tests { func test_noCheckmarkShownForMessageInPreview_whenThreadReplyReadByParticipant() throws { linkToScenario(withId: 432) - throw XCTSkip("Check out SWUI-245") - GIVEN("user opens the channel") { userRobot .login() @@ -242,10 +222,10 @@ extension MessageDeliveryStatus_ChannelList_Tests { participantRobot.sendMessage(message) } AND("user replies to message in thread") { - userRobot.replyToMessageInThread(threadReply) + userRobot.sendMessageInThread(threadReply) } AND("participant reads the user's thread reply") { - participantRobot.readMessageAfterDelay() + participantRobot.readMessage() } WHEN("user retuns to the channel list") { userRobot.moveToChannelListFromThreadReplies() @@ -263,8 +243,6 @@ extension MessageDeliveryStatus_ChannelList_Tests { func test_noCheckmarkShownForMessageInPreview_whenThreadReplyIsSentAndReadEventsIsDisabled() throws { linkToScenario(withId: 433) - throw XCTSkip("Check out SWUI-245") - GIVEN("user opens chat") { backendRobot.setReadEvents(to: false) userRobot @@ -275,7 +253,7 @@ extension MessageDeliveryStatus_ChannelList_Tests { participantRobot.sendMessage(message) } AND("user replies to message in thread") { - userRobot.replyToMessageInThread(threadReply) + userRobot.sendMessageInThread(threadReply) } WHEN("user retuns to the channel list") { userRobot.moveToChannelListFromThreadReplies() @@ -291,8 +269,6 @@ extension MessageDeliveryStatus_ChannelList_Tests { func test_noCheckmarkShownForMessageInPreview_whenThreadReplyIsSentByParticipant() throws { linkToScenario(withId: 434) - throw XCTSkip("Check out SWUI-245") - GIVEN("user opens chat") { userRobot .login() @@ -302,7 +278,7 @@ extension MessageDeliveryStatus_ChannelList_Tests { participantRobot.sendMessage(message) } AND("participant replies to message in thread") { - participantRobot.replyToMessageInThread(threadReply) + participantRobot.sendMessageInThread(threadReply) } WHEN("user retuns to the channel list") { userRobot.moveToChannelListFromThreadReplies() diff --git a/StreamChatSwiftUITestsAppTests/Tests/Message Delivery Status/MessageDeliveryStatus_Tests.swift b/StreamChatSwiftUITestsAppTests/Tests/Message Delivery Status/MessageDeliveryStatus_Tests.swift index e33ae7254..013fb72cc 100644 --- a/StreamChatSwiftUITestsAppTests/Tests/Message Delivery Status/MessageDeliveryStatus_Tests.swift +++ b/StreamChatSwiftUITestsAppTests/Tests/Message Delivery Status/MessageDeliveryStatus_Tests.swift @@ -13,19 +13,11 @@ final class MessageDeliveryStatus_Tests: StreamTestCase { var pendingThreadReply: String { "pending \(threadReply)" } var failedThreadReply: String { "failed \(threadReply)" } - override func setUpWithError() throws { - try super.setUpWithError() - addTags([.messageDeliveryStatus]) - assertMockServer() - } - // MARK: Message List func test_singleCheckmarkShown_whenMessageIsSent() throws { linkToScenario(withId: 397) - throw XCTSkip("Check out SWUI-245") - GIVEN("user opens chat") { userRobot .login() @@ -44,15 +36,13 @@ final class MessageDeliveryStatus_Tests: StreamTestCase { func test_deliveryStatusShowsClocks_whenMessageIsInPendingState() throws { linkToScenario(withId: 398) - throw XCTSkip("Check out SWUI-245") - GIVEN("user opens the channel") { userRobot .login() .openChannel() } WHEN("user sends a new message") { - backendRobot.delayServerResponse(byTimeInterval: 5.0) + backendRobot.delayNewMessages(by: 5) userRobot.sendMessage(pendingMessage, waitForAppearance: false) } THEN("message delivery status shows clocks") { @@ -62,8 +52,8 @@ final class MessageDeliveryStatus_Tests: StreamTestCase { func test_errorIndicatorShown_whenMessageFailedToBeSent() throws { linkToScenario(withId: 399) - - throw XCTSkip("Check out SWUI-245") + + throw XCTSkip("https://linear.app/stream/issue/IOS-1315") GIVEN("user becomes offline") { userRobot @@ -88,8 +78,6 @@ final class MessageDeliveryStatus_Tests: StreamTestCase { func test_doubleCheckmarkShown_whenMessageReadByParticipant() throws { linkToScenario(withId: 400) - throw XCTSkip("Check out SWUI-245") - GIVEN("user opens the channel") { userRobot .login() @@ -99,7 +87,7 @@ final class MessageDeliveryStatus_Tests: StreamTestCase { userRobot.sendMessage(message) } WHEN("participant reads the user's message") { - participantRobot.readMessageAfterDelay() + participantRobot.readMessage() } THEN("user spots double checkmark below the message") { userRobot.assertMessageDeliveryStatus(.read) @@ -111,8 +99,8 @@ final class MessageDeliveryStatus_Tests: StreamTestCase { func test_doubleCheckmarkShown_whenNewParticipantAdded() throws { linkToScenario(withId: 401) - - throw XCTSkip("Check out SWUI-245") + + throw XCTSkip("https://linear.app/stream/issue/IOS-1316") GIVEN("user opens the channel") { userRobot @@ -123,7 +111,7 @@ final class MessageDeliveryStatus_Tests: StreamTestCase { userRobot.sendMessage(message) } AND("message is read by more than 1 participant") { - participantRobot.readMessageAfterDelay() + participantRobot.readMessage() userRobot .assertMessageDeliveryStatus(.read) .assertMessageReadCount(readBy: 1) @@ -141,10 +129,8 @@ final class MessageDeliveryStatus_Tests: StreamTestCase { func test_readByDecremented_whenParticipantIsRemoved() throws { linkToScenario(withId: 402) - - throw XCTSkip("Check out SWUI-245") - - let participantOne = participantRobot.currentUserId + + throw XCTSkip("https://linear.app/stream/issue/IOS-1316") GIVEN("user opens the channel") { userRobot @@ -155,13 +141,13 @@ final class MessageDeliveryStatus_Tests: StreamTestCase { userRobot.sendMessage(message) } AND("is read by participant") { - participantRobot.readMessageAfterDelay() + participantRobot.readMessage() userRobot .assertMessageDeliveryStatus(.read) .assertMessageReadCount(readBy: 1) } WHEN("participant is removed from the channel") { -// userRobot.removeParticipant(withUserId: participantOne) +// userRobot.removeParticipant(withUserId: participantRobot.id) } THEN("user spots single checkmark below the message") { userRobot.assertMessageDeliveryStatus(.sent) @@ -170,10 +156,9 @@ final class MessageDeliveryStatus_Tests: StreamTestCase { func test_deliveryStatusShownForTheLastMessageInGroup() throws { linkToScenario(withId: 403) + let secondMessage = "second message" - throw XCTSkip("Check out SWUI-245") - GIVEN("user opens the channel") { userRobot .login() @@ -199,8 +184,6 @@ final class MessageDeliveryStatus_Tests: StreamTestCase { func test_deliveryStatusHidden_whenMessageIsDeleted() throws { linkToScenario(withId: 404) - throw XCTSkip("Check out SWUI-245") - GIVEN("user opens the channel") { userRobot .login() @@ -231,8 +214,6 @@ extension MessageDeliveryStatus_Tests { func test_singleCheckmarkShown_whenMessageIsSent_andPreviewedInThread() throws { linkToScenario(withId: 405) - throw XCTSkip("Check out SWUI-245") - GIVEN("user opens chat") { userRobot .login() @@ -253,8 +234,8 @@ extension MessageDeliveryStatus_Tests { func test_errorIndicatorShown_whenMessageFailedToBeSent_andCantBePreviewedInThread() throws { linkToScenario(withId: 406) - - throw XCTSkip("Check out SWUI-245") + + throw XCTSkip("https://linear.app/stream/issue/IOS-1315") GIVEN("user becomes offline") { userRobot @@ -281,8 +262,8 @@ extension MessageDeliveryStatus_Tests { func test_doubleCheckmarkShown_whenMessageReadByParticipant_andPreviewedInThread() throws { linkToScenario(withId: 407) - - throw XCTSkip("Check out SWUI-245") + + throw XCTSkip("https://linear.app/stream/issue/IOS-46") GIVEN("user opens the channel") { userRobot @@ -296,7 +277,7 @@ extension MessageDeliveryStatus_Tests { userRobot.openThread() } WHEN("the message is read by participant") { - participantRobot.readMessageAfterDelay() + participantRobot.readMessage() } THEN("user spots double checkmark below the message in thread") { userRobot.assertMessageDeliveryStatus(.read) @@ -311,8 +292,6 @@ extension MessageDeliveryStatus_Tests { func test_singleCheckmarkShown_whenThreadReplyIsSent() throws { linkToScenario(withId: 408) - throw XCTSkip("Check out SWUI-245") - GIVEN("user opens chat") { userRobot .login() @@ -322,7 +301,7 @@ extension MessageDeliveryStatus_Tests { participantRobot.sendMessage(message) } AND("user replies to the message in thread") { - userRobot.replyToMessageInThread(threadReply) + userRobot.sendMessageInThread(threadReply) } THEN("user spots single checkmark below the thread reply") { userRobot @@ -333,11 +312,11 @@ extension MessageDeliveryStatus_Tests { func test_errorIndicatorShown_whenThreadReplyFailedToBeSent() throws { linkToScenario(withId: 409) - - throw XCTSkip("Check out SWUI-245") + + throw XCTSkip("https://linear.app/stream/issue/IOS-1315") GIVEN("user becomes offline") { - backendRobot.generateChannels(count: 1, messagesCount: 1) + backendRobot.generateChannels(channelsCount: 1, messagesCount: 1) userRobot .setConnectivitySwitchVisibility(to: .on) .login() @@ -345,7 +324,7 @@ extension MessageDeliveryStatus_Tests { .openChannel() } WHEN("user replies to message in thread") { - userRobot.replyToMessageInThread(failedThreadReply, waitForAppearance: false) + userRobot.sendMessageInThread(failedThreadReply, waitForAppearance: false) } THEN("error indicator is shown for the thread reply") { userRobot.assertThreadReplyFailedToBeSent() @@ -359,8 +338,8 @@ extension MessageDeliveryStatus_Tests { func test_doubleCheckmarkShown_whenThreadReplyReadByParticipant() throws { linkToScenario(withId: 410) - - throw XCTSkip("Check out SWUI-245") + + throw XCTSkip("https://linear.app/stream/issue/IOS-46") GIVEN("user opens the channel") { userRobot @@ -371,10 +350,10 @@ extension MessageDeliveryStatus_Tests { participantRobot.sendMessage(message) } WHEN("user replies to message in thread") { - userRobot.replyToMessageInThread(threadReply) + userRobot.sendMessageInThread(threadReply) } AND("participant reads the user's thread reply") { - participantRobot.readMessageAfterDelay() + participantRobot.readMessage() } THEN("user spots double checkmark below the message") { userRobot.assertMessageDeliveryStatus(.read) @@ -386,8 +365,8 @@ extension MessageDeliveryStatus_Tests { func test_doubleCheckmarkShownInThreadReply_whenNewParticipantAdded() throws { linkToScenario(withId: 411) - - throw XCTSkip("Check out SWUI-245") + + throw XCTSkip("https://linear.app/stream/issue/IOS-1316") GIVEN("user opens the channel") { userRobot @@ -398,7 +377,7 @@ extension MessageDeliveryStatus_Tests { participantRobot.sendMessage(message) } AND("user replies to message in thread") { - userRobot.replyToMessageInThread(threadReply) + userRobot.sendMessageInThread(threadReply) } WHEN("new participant is added to the channel") { // userRobot.addParticipant() @@ -413,10 +392,8 @@ extension MessageDeliveryStatus_Tests { func test_readByDecrementedInThreadReply_whenParticipantIsRemoved() throws { linkToScenario(withId: 412) - - throw XCTSkip("Check out SWUI-245") - - let participantOne = participantRobot.currentUserId + + throw XCTSkip("https://linear.app/stream/issue/IOS-1316") GIVEN("user opens the channel") { userRobot @@ -427,16 +404,16 @@ extension MessageDeliveryStatus_Tests { participantRobot.sendMessage(message) } AND("user replies to message in thread") { - userRobot.replyToMessageInThread(threadReply) + userRobot.sendMessageInThread(threadReply) } AND("thread reply is read by participant") { - participantRobot.readMessageAfterDelay() + participantRobot.readMessage() userRobot .assertMessageDeliveryStatus(.read) .assertMessageReadCount(readBy: 1) } WHEN("participant is removed from the channel") { -// userRobot.removeParticipant(withUserId: participantOne) +// userRobot.removeParticipant(withUserId: participantRobot.id) } THEN("user spots single checkmark below the message") { userRobot.assertMessageDeliveryStatus(.sent) @@ -446,8 +423,6 @@ extension MessageDeliveryStatus_Tests { func test_deliveryStatusShownForTheLastThreadReplyInGroup() throws { linkToScenario(withId: 413) - throw XCTSkip("Check out SWUI-245") - let secondMessage = "second message" GIVEN("user opens the channel") { @@ -459,7 +434,7 @@ extension MessageDeliveryStatus_Tests { participantRobot.sendMessage(message) } AND("user replies to message in thread") { - userRobot.replyToMessageInThread(threadReply) + userRobot.sendMessageInThread(threadReply) } AND("delivery status shows single checkmark") { userRobot.assertMessageDeliveryStatus(.sent) @@ -478,8 +453,6 @@ extension MessageDeliveryStatus_Tests { func test_deliveryStatusHidden_whenThreadReplyIsDeleted() throws { linkToScenario(withId: 414) - throw XCTSkip("Check out SWUI-245") - GIVEN("user opens the channel") { userRobot .login() @@ -489,7 +462,7 @@ extension MessageDeliveryStatus_Tests { participantRobot.sendMessage(message) } AND("user replies to message in thread") { - userRobot.replyToMessageInThread(threadReply) + userRobot.sendMessageInThread(threadReply) } AND("delivery status shows single checkmark") { userRobot.assertMessageDeliveryStatus(.sent) @@ -507,8 +480,6 @@ extension MessageDeliveryStatus_Tests { func test_deliveryStatusShownForPreviousMessage_whenErrorMessageShown() throws { linkToScenario(withId: 415) - throw XCTSkip("Check out SWUI-245") - GIVEN("user opens the channel") { userRobot .login() @@ -538,8 +509,6 @@ extension MessageDeliveryStatus_Tests { func test_deliveryStatusHidden_whenMessageIsSentAndReadEventsIsDisabled() throws { linkToScenario(withId: 416) - throw XCTSkip("Check out SWUI-245") - GIVEN("user opens chat") { backendRobot.setReadEvents(to: false) userRobot @@ -558,8 +527,8 @@ extension MessageDeliveryStatus_Tests { func test_deliveryStatusShowsClocks_whenMessageIsInPendingStateAndReadEventsIsDisabled() throws { linkToScenario(withId: 417) - - throw XCTSkip("Check out SWUI-245") + + throw XCTSkip("https://linear.app/stream/issue/IOS-970") GIVEN("user opens the channel") { backendRobot.setReadEvents(to: false) @@ -568,7 +537,7 @@ extension MessageDeliveryStatus_Tests { .openChannel() } WHEN("user sends a new message") { - backendRobot.delayServerResponse(byTimeInterval: 5.0) + backendRobot.delayNewMessages(by: 5) userRobot.sendMessage(pendingMessage, waitForAppearance: false) } THEN("message delivery status shows clocks") { @@ -580,8 +549,8 @@ extension MessageDeliveryStatus_Tests { func test_errorIndicatorShown_whenMessageFailedToBeSentAndReadEventsIsDisabled() throws { linkToScenario(withId: 418) - - throw XCTSkip("Check out SWUI-245") + + throw XCTSkip("https://linear.app/stream/issue/IOS-1315") GIVEN("user becomes offline") { backendRobot.setReadEvents(to: false) @@ -607,8 +576,6 @@ extension MessageDeliveryStatus_Tests { func test_deliveryStatusHidden_whenMessageReadByParticipantAndReadEventsIsDisabled() throws { linkToScenario(withId: 419) - throw XCTSkip("Check out SWUI-245") - GIVEN("user opens the channel") { backendRobot.setReadEvents(to: false) userRobot @@ -619,7 +586,7 @@ extension MessageDeliveryStatus_Tests { userRobot.sendMessage(message) } WHEN("participant reads the user's message") { - participantRobot.readMessageAfterDelay() + participantRobot.readMessage() } THEN("delivery status is hidden") { userRobot @@ -630,8 +597,8 @@ extension MessageDeliveryStatus_Tests { func test_deliveryStatusHidden_whenNewParticipantAddedAndReadEventsIsDisabled() throws { linkToScenario(withId: 420) - - throw XCTSkip("Check out SWUI-245") + + throw XCTSkip("https://linear.app/stream/issue/IOS-1316") GIVEN("user opens the channel") { backendRobot.setReadEvents(to: false) @@ -643,7 +610,7 @@ extension MessageDeliveryStatus_Tests { userRobot.sendMessage(message) } AND("message is read by more than 1 participant") { - participantRobot.readMessageAfterDelay() + participantRobot.readMessage() } WHEN("new participant is added to the channel") { // userRobot.addParticipant() @@ -657,10 +624,8 @@ extension MessageDeliveryStatus_Tests { func test_deliveryStatusHidden_whenParticipantIsRemovedAndReadEventsIsDisabled() throws { linkToScenario(withId: 421) - - throw XCTSkip("Check out SWUI-245") - - let participantOne = participantRobot.currentUserId + + throw XCTSkip("https://linear.app/stream/issue/IOS-1316") GIVEN("user opens the channel") { backendRobot.setReadEvents(to: false) @@ -672,10 +637,10 @@ extension MessageDeliveryStatus_Tests { userRobot.sendMessage(message) } AND("is read by participant") { - participantRobot.readMessageAfterDelay() + participantRobot.readMessage() } WHEN("participant is removed from the channel") { -// userRobot.removeParticipant(withUserId: participantOne) +// userRobot.removeParticipant(withUserId: participantRobot.id) } AND("delivery status is hidden") { userRobot.assertMessageDeliveryStatus(nil) @@ -688,8 +653,6 @@ extension MessageDeliveryStatus_Tests { func test_deliveryStatusHiddenForMessagesInGroup_whenReadEventsIsDisabled() throws { linkToScenario(withId: 422) - throw XCTSkip("Check out SWUI-245") - let secondMessage = "second message" GIVEN("user opens the channel") { @@ -718,8 +681,6 @@ extension MessageDeliveryStatus_Tests { func test_deliveryStatusHidden_whenMessageIsDeletedAndReadEventsIsDisabled() throws { linkToScenario(withId: 423) - throw XCTSkip("Check out SWUI-245") - GIVEN("user opens the channel") { backendRobot.setReadEvents(to: false) userRobot diff --git a/StreamChatSwiftUITestsAppTests/Tests/MessageList_Tests.swift b/StreamChatSwiftUITestsAppTests/Tests/MessageList_Tests.swift index 7cdce68c5..a527b695c 100644 --- a/StreamChatSwiftUITestsAppTests/Tests/MessageList_Tests.swift +++ b/StreamChatSwiftUITestsAppTests/Tests/MessageList_Tests.swift @@ -5,12 +5,6 @@ import XCTest final class MessageList_Tests: StreamTestCase { - override func setUpWithError() throws { - try super.setUpWithError() - addTags([.coreFeatures]) - assertMockServer() - } - func test_messageListUpdates_whenUserSendsMessage() { linkToScenario(withId: 237) @@ -40,7 +34,7 @@ final class MessageList_Tests: StreamTestCase { .openChannel() } WHEN("participant sends a message") { - participantRobot.sendMessage(message, waitBeforeSending: 0.5) + participantRobot.sendMessage(message) } THEN("MessageList updates for user") { userRobot.assertMessage(message) @@ -109,7 +103,7 @@ final class MessageList_Tests: StreamTestCase { userRobot.login().openChannel() } WHEN("participant sends a message: '\(message)'") { - participantRobot.sendMessage(message, waitBeforeSending: 0.5) + participantRobot.sendMessage(message) } THEN("the message is delivered") { userRobot.assertMessageAuthor(author) @@ -126,7 +120,7 @@ final class MessageList_Tests: StreamTestCase { userRobot.login().openChannel() } WHEN("participant sends the message: '\(message)'") { - participantRobot.sendMessage(message, waitBeforeSending: 0.5) + participantRobot.sendMessage(message) } AND("participant edits the message: '\(editedMessage)'") { participantRobot.editMessage(editedMessage) @@ -188,35 +182,30 @@ final class MessageList_Tests: StreamTestCase { func test_composerGrowthLimit() throws { linkToScenario(withId: 260) - throw XCTSkip("Check out SWUI-188") - GIVEN("user opens the channel") { userRobot .login() .openChannel() } - THEN("user verifies that composer does not grow more than 5 lines") { - userRobot.assertComposerLimits(toNumberOfLines: 5) + THEN("user verifies that composer does not grow more than 4 lines") { + userRobot.assertComposerLimits(toNumberOfLines: 4) } } func test_typingIndicator() throws { linkToScenario(withId: 358) - let typingEventsTimeout: Double = 4 - GIVEN("user opens the channel") { userRobot.login().openChannel() } WHEN("participant starts typing") { - participantRobot.wait(typingEventsTimeout).startTyping() + participantRobot.startTyping() } THEN("user observes typing indicator is shown") { - let typingUserName = UserDetails.userName(for: participantRobot.currentUserId) - userRobot.assertTypingIndicatorShown(typingUserName: typingUserName) + userRobot.assertTypingIndicatorShown(typingUserName: participantRobot.name) } WHEN("participant stops typing") { - participantRobot.wait(typingEventsTimeout).stopTyping() + participantRobot.stopTyping() } THEN("user observes typing indicator has disappeared") { userRobot.assertTypingIndicatorHidden() @@ -226,10 +215,8 @@ final class MessageList_Tests: StreamTestCase { func test_commandsPopupDisappear_whenUserTapsOnMessageList() throws { linkToScenario(withId: 364) - throw XCTSkip("Check out SWUI-187") - GIVEN("user opens the channel") { - backendRobot.generateChannels(count: 1, messagesCount: 30) + backendRobot.generateChannels(channelsCount: 1, messagesCount: 30) userRobot.login().openChannel() } AND("user opens command suggestions") { @@ -245,13 +232,13 @@ final class MessageList_Tests: StreamTestCase { func test_offlineMessageInTheMessageList() throws { linkToScenario(withId: 365) - - throw XCTSkip("Check out SWUI-245") + + throw XCTSkip("https://linear.app/stream/issue/IOS-1315") let message = "test message" GIVEN("user opens the channel") { - backendRobot.generateChannels(count: 1, messagesCount: 40) + backendRobot.generateChannels(channelsCount: 1, messagesCount: 40) userRobot .setConnectivitySwitchVisibility(to: .on) .login() @@ -273,8 +260,8 @@ final class MessageList_Tests: StreamTestCase { func test_addMessageWhileOffline() throws { linkToScenario(withId: 366) - - throw XCTSkip("Check out SWUI-245") + + throw XCTSkip("https://linear.app/stream/issue/IOS-1315") let message = "test message" @@ -315,7 +302,7 @@ final class MessageList_Tests: StreamTestCase { func test_offlineRecoveryWithinSession() throws { linkToScenario(withId: 367) - throw XCTSkip("Check out SWUI-245") + throw XCTSkip("https://linear.app/stream/issue/IOS-1315") let message = "test message" @@ -330,7 +317,7 @@ final class MessageList_Tests: StreamTestCase { deviceRobot.moveApplication(to: .background) } WHEN("participant sends a new message") { - participantRobot.wait(1).sendMessage(message) + participantRobot.sleep(1).sendMessage(message) } AND("user comes back to the foreground") { deviceRobot.moveApplication(to: .foreground) @@ -361,12 +348,10 @@ extension MessageList_Tests { func test_messageListScrollsDown_whenMessageListIsScrolledUp_andUserSendsNewMessage() throws { linkToScenario(withId: 359) - throw XCTSkip("Check out SWUI-256") - let newMessage = "New message" GIVEN("user opens the channel") { - backendRobot.generateChannels(count: 1, messagesCount: 30) + backendRobot.generateChannels(channelsCount: 1, messagesCount: 30) userRobot.login().openChannel() } WHEN("user scrolls up") { @@ -388,7 +373,7 @@ extension MessageList_Tests { let newMessage = "New message" GIVEN("user opens the channel") { - backendRobot.generateChannels(count: 1, messagesCount: 30) + backendRobot.generateChannels(channelsCount: 1, messagesCount: 30) userRobot.login().openChannel() } WHEN("participant sends a message") { @@ -407,7 +392,7 @@ extension MessageList_Tests { let newMessage = "New message" GIVEN("user opens the channel") { - backendRobot.generateChannels(count: 1, messagesCount: 30) + backendRobot.generateChannels(channelsCount: 1, messagesCount: 30) userRobot.login().openChannel() } WHEN("user scrolls up") { @@ -429,7 +414,7 @@ extension MessageList_Tests { let newMessage = "New message" GIVEN("user opens the channel") { - backendRobot.generateChannels(count: 1, messagesCount: 30) + backendRobot.generateChannels(channelsCount: 1, messagesCount: 30) userRobot.login().openChannel() } AND("user sends a new message") { @@ -452,14 +437,14 @@ extension MessageList_Tests { linkToScenario(withId: 363) GIVEN("user opens the channel") { - backendRobot.generateChannels(count: 1, messagesCount: 30) + backendRobot.generateChannels(channelsCount: 1, messagesCount: 30) userRobot.login().openChannel() } AND("user scrolls up") { userRobot.scrollMessageListUpSlow() } AND("participant sends some messages") { - participantRobot.sendMultipleMessages(repeatingText: "Some message", count: 16) + participantRobot.sendMultipleMessages("Some message", count: 16) } WHEN("user scrolls to the bottom") { userRobot.tapOnScrollToBottomButton() @@ -477,7 +462,7 @@ extension MessageList_Tests { let newMessage = "New message" GIVEN("user opens the channel") { - backendRobot.generateChannels(count: 1, messagesCount: 30) + backendRobot.generateChannels(channelsCount: 1, messagesCount: 30) userRobot.login().openChannel() } WHEN("user scrolls up") { @@ -515,7 +500,7 @@ extension MessageList_Tests { let messagesCount = 60 WHEN("user opens the channel") { - backendRobot.generateChannels(count: 1, messagesCount: messagesCount) + backendRobot.generateChannels(channelsCount: 1, messagesCount: messagesCount) userRobot.login().openChannel() } THEN("user makes sure that chat history is loaded") { @@ -526,16 +511,16 @@ extension MessageList_Tests { func test_paginationOnThread() throws { linkToScenario(withId: 371) - throw XCTSkip("https://github.com/GetStream/ios-issues-tracking/issues/854") + throw XCTSkip("https://linear.app/stream/issue/IOS-479") let replyCount = 60 GIVEN("user opens the channel") { - backendRobot.generateChannels(count: 1, messagesCount: 1, replyCount: replyCount) + backendRobot.generateChannels(channelsCount: 1, messagesCount: 1, repliesCount: replyCount) userRobot.login().openChannel() } WHEN("user opens the thread") { - userRobot.openThread() + userRobot.openThread(waitForThreadIcon: true) } THEN("user makes sure that thread history is loaded") { userRobot.assertThreadListPagination(messagesCount: replyCount + 1) @@ -549,7 +534,7 @@ extension MessageList_Tests { func test_addingCommandHidesLeftButtons() throws { linkToScenario(withId: 372) - throw XCTSkip("Check out SWUI-243") + throw XCTSkip("https://linear.app/stream/issue/IOS-231") GIVEN("user opens the channel") { userRobot.login().openChannel() @@ -595,10 +580,10 @@ extension MessageList_Tests { userRobot.login().openChannel() } WHEN("user taps on participants name") { - userRobot.mentionParticipant() + userRobot.mentionParticipant(participantRobot.id) } THEN("composer fills in participants name") { - userRobot.assertMentionWasApplied() + userRobot.assertMentionWasApplied(userName: participantRobot.name) } } } @@ -682,20 +667,18 @@ extension MessageList_Tests { extension MessageList_Tests { func test_threadReplyAppearsInThread_whenParticipantAddsThreadReply() throws { linkToScenario(withId: 379) - - throw XCTSkip("XCTest issue: Automation type mismatch: computed Other from legacy attributes vs ActivityIndicator from modern attribute.") let threadReply = "thread reply" GIVEN("user opens the channel") { - backendRobot.generateChannels(count: 1, messagesCount: 1) + backendRobot.generateChannels(channelsCount: 1, messagesCount: 1) userRobot.login().openChannel() } WHEN("participant adds a thread reply") { - participantRobot.replyToMessageInThread(threadReply) + participantRobot.sendMessageInThread(threadReply) } AND("user enters thread") { - userRobot.openThread() + userRobot.openThread(waitForThreadIcon: true) } THEN("user observes the thread reply in thread") { userRobot.assertThreadReply(threadReply) @@ -708,11 +691,11 @@ extension MessageList_Tests { let threadReply = "thread reply" GIVEN("user opens the channel") { - backendRobot.generateChannels(count: 1, messagesCount: 1) + backendRobot.generateChannels(channelsCount: 1, messagesCount: 1) userRobot.login().openChannel() } WHEN("participant adds a thread reply") { - participantRobot.replyToMessageInThread(threadReply, alsoSendInChannel: true) + participantRobot.sendMessageInThread(threadReply, alsoSendInChannel: true) } THEN("user observes the thread reply in channel") { userRobot.assertMessage(threadReply) @@ -731,11 +714,11 @@ extension MessageList_Tests { let threadReply = "thread reply" GIVEN("user opens the channel") { - backendRobot.generateChannels(count: 1, messagesCount: 1) + backendRobot.generateChannels(channelsCount: 1, messagesCount: 1) userRobot.login().openChannel() } WHEN("user adds a thread reply and sends it also to main channel") { - userRobot.replyToMessageInThread(threadReply, alsoSendInChannel: true) + userRobot.sendMessageInThread(threadReply, alsoSendInChannel: true) } THEN("user observes the thread reply in thread") { userRobot.assertThreadReply(threadReply) @@ -750,24 +733,23 @@ extension MessageList_Tests { func test_threadTypingIndicatorHidden_whenParticipantStopsTyping() throws { linkToScenario(withId: 382) - throw XCTSkip("Check out SWUI-251") + throw XCTSkip("https://linear.app/stream/issue/IOS-239") GIVEN("user opens the channel") { - backendRobot.generateChannels(count: 1, messagesCount: 1) + backendRobot.generateChannels(channelsCount: 1, messagesCount: 1) userRobot.login().openChannel() } AND("user opens the thread") { userRobot.openThread() } WHEN("participant starts typing in thread") { - participantRobot.wait(2).startTypingInThread() + participantRobot.startTypingInThread() } THEN("user observes typing indicator is shown") { - let typingUserName = UserDetails.userName(for: participantRobot.currentUserId) - userRobot.assertTypingIndicatorShown(typingUserName: typingUserName) + userRobot.assertTypingIndicatorShown(typingUserName: participantRobot.name) } WHEN("participant stops typing in thread") { - participantRobot.wait(2).stopTypingInThread() + participantRobot.stopTypingInThread() } THEN("user observes typing indicator has disappeared") { userRobot.assertTypingIndicatorHidden() @@ -782,7 +764,7 @@ extension MessageList_Tests { linkToScenario(withId: 383) let message = "Hey there" - let messageWithForbiddenContent = server.forbiddenWords.first ?? "" + let messageWithForbiddenContent = mockServer.forbiddenWord GIVEN("user opens the channel") { userRobot @@ -821,7 +803,7 @@ extension MessageList_Tests { } WHEN("user sends an ephemeral message") { userRobot - .sendGiphy(send: false) + .uploadGiphy(send: false) .scrollMessageListDown() // to hide the keyboard } THEN("messages are not grouped, 1st message shows the timestamp") { @@ -830,32 +812,6 @@ extension MessageList_Tests { .assertMessageHasTimestamp(at: 1) } } - - func test_messageRendersTimestampAgain_whenMessageLastInGroupIsHardDeleted() throws { - linkToScenario(withId: 385) - - throw XCTSkip("Check out SWUI-245") - - GIVEN("user opens the channel") { - backendRobot - .generateChannels(count: 1, messagesCount: 1) - userRobot - .login() - .openChannel() - } - AND("user inserts 3 group messages") { - userRobot.sendMessage("Hey") - userRobot.sendMessage("Hey2") - userRobot.sendMessage("Hey3") - userRobot.assertMessageHasTimestamp() - } - WHEN("user deletes last message") { - userRobot.deleteMessage(hard: true) - } - THEN("previous message should re-render timestamp") { - userRobot.assertMessageHasTimestamp(at: 0) - } - } } // MARK: Deleted messages @@ -864,8 +820,6 @@ extension MessageList_Tests { func test_deletesMessage() throws { linkToScenario(withId: 386) - throw XCTSkip("Check out SWUI-254") - let message = "test message" GIVEN("user opens the channel") { @@ -891,7 +845,7 @@ extension MessageList_Tests { userRobot.login().openChannel() } WHEN("participant sends the message: '\(message)'") { - participantRobot.sendMessage(message, waitBeforeSending: 0.5) + participantRobot.sendMessage(message) } AND("participant deletes the message: '\(message)'") { participantRobot.deleteMessage() @@ -907,11 +861,11 @@ extension MessageList_Tests { let threadReply = "thread reply" GIVEN("user opens the channel") { - backendRobot.generateChannels(count: 1, messagesCount: 1) + backendRobot.generateChannels(channelsCount: 1, messagesCount: 1) userRobot.login().openChannel() } AND("participant adds a thread reply and sends it also to main channel") { - participantRobot.replyToMessageInThread(threadReply, alsoSendInChannel: true) + participantRobot.sendMessageInThread(threadReply, alsoSendInChannel: true) } WHEN("participant removes the thread reply from channel") { participantRobot.deleteMessage() @@ -932,11 +886,11 @@ extension MessageList_Tests { let threadReply = "thread reply" GIVEN("user opens the channel") { - backendRobot.generateChannels(count: 1, messagesCount: 1) + backendRobot.generateChannels(channelsCount: 1, messagesCount: 1) userRobot.login().openChannel() } AND("user adds a thread reply and sends it also to main channel") { - userRobot.replyToMessageInThread(threadReply, alsoSendInChannel: true) + userRobot.sendMessageInThread(threadReply, alsoSendInChannel: true) } WHEN("user removes thread reply from thread") { userRobot.deleteMessage() @@ -957,17 +911,17 @@ extension MessageList_Tests { let threadReply = "thread reply" GIVEN("user opens the channel") { - backendRobot.generateChannels(count: 1, messagesCount: 1) + backendRobot.generateChannels(channelsCount: 1, messagesCount: 1) userRobot.login().openChannel() } AND("participant adds a thread reply") { - participantRobot.replyToMessageInThread(threadReply, alsoSendInChannel: false) + participantRobot.sendMessageInThread(threadReply, alsoSendInChannel: false) } WHEN("participant removes the thread reply") { participantRobot.deleteMessage() } THEN("user observes a thread reply count button in channel") { - userRobot.assertThreadReplyCountButton() + userRobot.assertThreadReplyCountButton(replies: 1) } THEN("user observes the thread reply removed in thread") { userRobot.openThread().assertDeletedMessage() @@ -977,16 +931,14 @@ extension MessageList_Tests { func test_threadReplyIsRemovedEverywhere_whenUserRemovesItFromThread() throws { linkToScenario(withId: 393) - throw XCTSkip("Check out SWUI-244") - let threadReply = "thread reply" GIVEN("user opens the channel") { - backendRobot.generateChannels(count: 1, messagesCount: 1) + backendRobot.generateChannels(channelsCount: 1, messagesCount: 1) userRobot.login().openChannel() } AND("user adds a thread reply and sends it also to main channel") { - userRobot.replyToMessageInThread(threadReply, alsoSendInChannel: true) + userRobot.sendMessageInThread(threadReply, alsoSendInChannel: true) } WHEN("user goes back to channel and removes thread reply") { userRobot @@ -1006,16 +958,14 @@ extension MessageList_Tests { func test_userRemovesThreadReply() throws { linkToScenario(withId: 394) - throw XCTSkip("Check out SWUI-244") - let threadReply = "thread reply" GIVEN("user opens the channel") { - backendRobot.generateChannels(count: 1, messagesCount: 1) + backendRobot.generateChannels(channelsCount: 1, messagesCount: 1) userRobot.login().openChannel() } AND("user adds a thread reply") { - userRobot.replyToMessageInThread(threadReply, alsoSendInChannel: false) + userRobot.sendMessageInThread(threadReply, alsoSendInChannel: false) } WHEN("user removes the thread reply") { userRobot.deleteMessage() @@ -1026,29 +976,7 @@ extension MessageList_Tests { AND("user observes a thread reply count button in channel") { userRobot .tapOnBackButton() - .assertThreadReplyCountButton() - } - } - - func test_hardDeletesMessage() throws { - linkToScenario(withId: 395) - - throw XCTSkip("Check out SWUI-245") - - let message = "test message" - - GIVEN("user opens the channel") { - backendRobot.generateChannels(count: 1, messagesCount: 1) - userRobot.login().openChannel() - } - WHEN("user sends the message: '\(message)'") { - userRobot.sendMessage(message) - } - AND("user hard-deletes the message: '\(message)'") { - userRobot.deleteMessage(hard: true) - } - THEN("the message is hard-deleted") { - userRobot.assertHardDeletedMessage(withText: message) + .assertThreadReplyCountButton(replies: 1) } } @@ -1058,7 +986,7 @@ extension MessageList_Tests { let message = "test message" GIVEN("user opens the channel") { - backendRobot.generateChannels(count: 1, messagesCount: 1) + backendRobot.generateChannels(channelsCount: 1, messagesCount: 1) userRobot.login().openChannel() } WHEN("participant sends the message: '\(message)'") { diff --git a/StreamChatSwiftUITestsAppTests/Tests/PushNotification_Tests.swift b/StreamChatSwiftUITestsAppTests/Tests/PushNotification_Tests.swift index 243c50266..579ca4d72 100644 --- a/StreamChatSwiftUITestsAppTests/Tests/PushNotification_Tests.swift +++ b/StreamChatSwiftUITestsAppTests/Tests/PushNotification_Tests.swift @@ -4,23 +4,15 @@ import XCTest -// Requires running a standalone Sinatra server final class PushNotification_Tests: StreamTestCase { - let sender = "Count Dooku" - let message = "How are you? 🙂" - - override func setUpWithError() throws { - try super.setUpWithError() - assertMockServer() - } - func test_pushNotificationFromMessageList() throws { linkToScenario(withId: 442) GIVEN("user goes to message list") { userRobot.login().openChannel() } - checkHappyPath(message: message, sender: sender) + + checkHappyPath() } func test_pushNotificationFromChannelList() throws { @@ -32,84 +24,43 @@ final class PushNotification_Tests: StreamTestCase { .openChannel() // this is required to let the mock server know .tapOnBackButton() // which channel to use for push notifications } - checkHappyPath(message: message, sender: sender) + checkHappyPath() } func test_pushNotification_optionalValuesEqualToNil() throws { linkToScenario(withId: 444) - mockPushNotification(body: message) - GIVEN("user goes to message list") { userRobot.login().openChannel() } - checkHappyPath(message: message, sender: app.label.uppercased()) + checkHappyPath(title: nil, rest: "null") } func test_pushNotification_optionalValuesAreEmpty() throws { linkToScenario(withId: 445) - mockPushNotification( - body: message, - title: "", - badge: 0, - mutableContent: 0, - category: "", - type: "", - sender: "", - version: "", - messageId: "", - cid: "" - ) - GIVEN("user goes to message list") { userRobot.login().openChannel() } - checkHappyPath(message: message, sender: app.label.uppercased()) + checkHappyPath(title: nil, rest: "empty") } func test_pushNotification_optionalValuesContainIncorrectType() throws { linkToScenario(withId: 446) - mockPushNotification( - body: message, - title: 42, - badge: "test", - mutableContent: "test", - category: 42, - type: 42, - sender: 42, - version: 42, - messageId: 42, - cid: 42 - ) - GIVEN("user goes to message list") { userRobot.login().openChannel() } - checkHappyPath(message: message, sender: app.label.uppercased()) + checkHappyPath(title: nil, rest: "incorrect_type") } func test_pushNotification_optionalValuesContainIncorrectData() throws { linkToScenario(withId: 447) - - mockPushNotification( - body: message, - title: -1, - badge: -1, - mutableContent: -1, - category: "test", - type: "test", - sender: "test", - version: "test", - messageId: "test", - cid: "test" - ) - + GIVEN("user goes to message list") { userRobot.login().openChannel() } - checkHappyPath(message: message, sender: app.label.uppercased()) + checkHappyPath(rest: "incorrect_data") } func test_pushNotification_requiredValuesAreInvalid() throws { @@ -121,108 +72,87 @@ final class PushNotification_Tests: StreamTestCase { AND("user goes to background") { deviceRobot.moveApplication(to: .background) } - - mockPushNotification(body: nil) WHEN("participant sends a message (push body param is nil)") { - participantRobot.wait(2).sendMessage( - "\(message)_0", - withPushNotification: true, - bundleIdForPushNotification: app.bundleId() - ) + let message = "null" + participantRobot + .sleep(1) + .sendMessage(message) + .sendPushNotification( + title: message, + body: message, + bundleId: app.bundleId(), + rest: message + ) } THEN("user does not receive a push notification") { userRobot.assertPushNotificationDoesNotAppear() } - - mockPushNotification(body: "") WHEN("participant sends a message (push body param is empty)") { - participantRobot.sendMessage( - "\(message)_1", - withPushNotification: true, - bundleIdForPushNotification: app.bundleId() - ) + let message = "empty" + participantRobot + .sendMessage(message) + .sendPushNotification( + title: message, + body: message, + bundleId: app.bundleId(), + rest: message + ) } THEN("user does not receive a push notification") { userRobot.assertPushNotificationDoesNotAppear() } - - mockPushNotification(body: 42) WHEN("participant sends a message (push body param contains incorrect type)") { - participantRobot.sendMessage( - "\(message)_2", - withPushNotification: true, - bundleIdForPushNotification: app.bundleId() - ) + let message = "42" + participantRobot + .sendMessage(message) + .sendPushNotification( + title: message, + body: message, + bundleId: app.bundleId(), + rest: "incorrect_type" + ) } THEN("user does not receive a push notification") { userRobot.assertPushNotificationDoesNotAppear() } - WHEN("user comes back to foreground") { deviceRobot.moveApplication(to: .foreground) } THEN("message list updates") { userRobot - .assertMessage("\(message)_0", at: 2) - .assertMessage("\(message)_1", at: 1) - .assertMessage("\(message)_2", at: 0) + .assertMessage("null", at: 2) + .assertMessage("empty", at: 1) + .assertMessage("42", at: 0) } } - func mockPushNotification( - body: Any?, - title: Any? = nil, - badge: Any? = nil, - mutableContent: Any? = nil, - category: Any? = nil, - type: Any? = nil, - sender: Any? = nil, - version: Any? = nil, - messageId: Any? = nil, - cid: Any? = nil - ) { - var json = TestData.toJson(.pushNotification) - - var aps = json[APNSKey.aps] as? [String: Any] - var alert = aps?[APNSKey.alert] as? [String: Any] - alert?[APNSKey.title] = title - alert?[APNSKey.body] = body - aps?[APNSKey.alert] = alert - aps?[APNSKey.badge] = badge - aps?[APNSKey.mutableContent] = mutableContent - aps?[APNSKey.category] = category - json[APNSKey.aps] = aps - - var stream = json[APNSKey.stream] as? [String: Any] - stream?[APNSKey.sender] = sender - stream?[APNSKey.type] = type - stream?[APNSKey.version] = version - stream?[APNSKey.messageId] = messageId - stream?[APNSKey.cid] = cid - json[APNSKey.stream] = stream - - server.pushNotificationPayload = json - } - - func checkHappyPath(message: String, sender: String) { + func checkHappyPath(title: String? = "Test title", body: String = "Test body", rest: String? = nil) { WHEN("user goes to background") { deviceRobot.moveApplication(to: .background) } AND("participant sends a message") { - participantRobot.wait(2).sendMessage( - message, - withPushNotification: true, - bundleIdForPushNotification: app.bundleId() - ) + participantRobot + .sleep(1) + .sendMessage(body) + .sendPushNotification( + title: title, + body: body, + bundleId: app.bundleId(), + rest: rest + ) } THEN("user receives a push notification") { - userRobot.assertPushNotification(withText: message, from: sender) + if let title { + userRobot.assertPushNotification(title: title, body: body) + } else { + userRobot.assertPushNotification(title: app.label, body: body) + } } WHEN("user taps on the push notification") { userRobot.tapOnPushNotification() } THEN("message list updates") { - userRobot.assertMessage(message) + userRobot.assertMessage(body) } } } diff --git a/StreamChatSwiftUITestsAppTests/Tests/QuotedReply_Tests.swift b/StreamChatSwiftUITestsAppTests/Tests/QuotedReply_Tests.swift index 44833c433..6ac8f1813 100644 --- a/StreamChatSwiftUITestsAppTests/Tests/QuotedReply_Tests.swift +++ b/StreamChatSwiftUITestsAppTests/Tests/QuotedReply_Tests.swift @@ -2,186 +2,190 @@ // Copyright © 2025 Stream.io Inc. All rights reserved. // +@testable import StreamChatSwiftUI import XCTest final class QuotedReply_Tests: StreamTestCase { let messageCount = 30 - let parentMessage = "1" - let quotedMessage = "quoted reply" - - override func setUpWithError() throws { - throw XCTSkip("At the moment, quoted replies do not work well enough on our testing framework") - - try super.setUpWithError() - addTags([.coreFeatures]) - assertMockServer() + let pageSize = 25 + let quotedText = "1" + let parentText = "test" + let replyText = "quoted reply" + + func test_whenSwipingMessage_thenMessageIsQuotedReply() { + linkToScenario(withId: 9854) + + GIVEN("user opens the channel") { + userRobot.login().openChannel() + } + WHEN("user swipes a message") { + participantRobot.sendMessage(parentText) + userRobot.swipeMessage() + } + THEN("user quoted the message") { + userRobot + .sendMessage("Quoting") + .assertQuotedMessage(parentText) + } } - - override func tearDownWithError() throws {} - + func test_quotedReplyInList_whenUserAddsQuotedReply() { linkToScenario(withId: 368) - + let messageCount = 20 - + GIVEN("user opens the channel") { - backendRobot.generateChannels(count: 1, messagesCount: 20) + backendRobot.generateChannels(channelsCount: 1, messagesCount: 20) userRobot.login().openChannel() } WHEN("user adds a quoted reply to participant message") { userRobot - .scrollMessageListUp(times: 4) - .quoteMessage(quotedMessage, messageCellIndex: messageCount - 1, waitForAppearance: false) - .waitForMessageVisibility(at: 0) + .scrollMessageListUp(times: 3) + .quoteMessage(replyText, messageCellIndex: messageCount - 1) } THEN("user observes the quote reply in message list") { userRobot - .assertQuotedMessage(replyText: quotedMessage, quotedText: parentMessage) .assertScrollToBottomButton(isVisible: false) + .assertQuotedMessage(replyText: replyText, quotedText: quotedText) } WHEN("user taps on a quoted message") { - userRobot.tapOnQuotedMessage(parentMessage, at: 0) + userRobot.tapOnQuotedMessage(quotedText, at: 0) } THEN("user is scrolled up to the quoted message") { userRobot - .assertMessageIsVisible(at: messageCount) .assertScrollToBottomButton(isVisible: true) + .assertMessageIsVisible(at: messageCount - 1) } } - + func test_quotedReplyInList_whenParticipantAddsQuotedReply_Message() { linkToScenario(withId: 369) - + let messageCount = 20 - + GIVEN("user opens the channel") { - backendRobot.generateChannels(count: 1, messagesCount: messageCount) + backendRobot.generateChannels(channelsCount: 1, messagesCount: messageCount) userRobot.login().openChannel() } WHEN("participant adds a quoted reply") { - participantRobot.quoteMessage(quotedMessage, toLastMessage: false) - userRobot.waitForMessageVisibility(at: 0) + participantRobot.quoteMessage(replyText, last: false) } THEN("user observes the quote reply in message list") { userRobot - .assertQuotedMessage(replyText: quotedMessage, quotedText: parentMessage) .assertScrollToBottomButton(isVisible: false) + .assertQuotedMessage(replyText: replyText, quotedText: quotedText) } WHEN("user taps on a quoted message") { - userRobot.tapOnQuotedMessage(parentMessage, at: 0) + userRobot.tapOnQuotedMessage(quotedText, at: 0) } THEN("user is scrolled up to the quoted message") { userRobot - .assertMessageIsVisible(at: messageCount) .assertScrollToBottomButton(isVisible: true) + .assertMessageIsVisible(at: messageCount - 1) } } - - func test_quotedReplyNotInList_whenUserAddsQuotedReply() throws { + + func test_quotedReplyNotInList_whenUserAddsQuotedReply() { linkToScenario(withId: 1701) - + GIVEN("user opens the channel") { - backendRobot.generateChannels(count: 1, messagesCount: messageCount) + backendRobot.generateChannels(channelsCount: 1, messagesCount: messageCount) userRobot.login().openChannel() } WHEN("user adds a quoted reply to participant message") { userRobot - .scrollMessageListUp(times: 4) - .quoteMessage(quotedMessage, messageCellIndex: messageCount - 1, waitForAppearance: false) - .waitForMessageVisibility(at: 0) + .scrollMessageListUp(times: 3) + .quoteMessage(replyText, messageCellIndex: messageCount - 1) } THEN("user observes the quote reply in message list") { userRobot - .assertQuotedMessage(replyText: quotedMessage, quotedText: parentMessage) .assertScrollToBottomButton(isVisible: false) + .assertQuotedMessage(replyText: replyText, quotedText: quotedText) } WHEN("user taps on a quoted message") { - userRobot.tapOnQuotedMessage(parentMessage, at: 0) + userRobot.tapOnQuotedMessage(quotedText, at: 0) } THEN("user is scrolled up to the quoted message") { userRobot - .assertMessageIsVisible(at: messageCount) .assertScrollToBottomButton(isVisible: true) + .assertFirstMessageIsVisible("1") } } - + func test_quotedReplyNotInList_whenParticipantAddsQuotedReply_Message() { linkToScenario(withId: 1702) - + GIVEN("user opens the channel") { - backendRobot.generateChannels(count: 1, messagesCount: messageCount) + backendRobot.generateChannels(channelsCount: 1, messagesCount: messageCount) userRobot.login().openChannel() } WHEN("participant adds a quoted reply") { - participantRobot.quoteMessage(quotedMessage, toLastMessage: false) - userRobot.waitForMessageVisibility(at: 0) + participantRobot.quoteMessage(replyText, last: false) } THEN("user observes the quote reply in message list") { userRobot - .assertQuotedMessage(replyText: quotedMessage, quotedText: parentMessage) .assertScrollToBottomButton(isVisible: false) + .assertQuotedMessage(replyText: replyText, quotedText: quotedText) } WHEN("user taps on a quoted message") { - userRobot.tapOnQuotedMessage(parentMessage, at: 0) + userRobot.tapOnQuotedMessage(quotedText, at: 0) } THEN("user is scrolled up to the quoted message") { userRobot - .assertMessageIsVisible(at: messageCount) .assertScrollToBottomButton(isVisible: true) + .assertFirstMessageIsVisible(quotedText) } } - + func test_quotedReplyNotInList_whenParticipantAddsQuotedReply_File() { linkToScenario(withId: 1703) - + GIVEN("user opens the channel") { - backendRobot.generateChannels(count: 1, messagesCount: messageCount) + backendRobot.generateChannels(channelsCount: 1, messagesCount: messageCount) userRobot.login().openChannel() } WHEN("participant sends file as a quoted reply") { - participantRobot.uploadAttachment(type: .file, asReplyToFirstMessage: true) - userRobot.waitForMessageVisibility(at: 0) + participantRobot.quoteMessageWithAttachment(type: .file, last: false) } THEN("user observes the quote reply in message list") { userRobot - .assertQuotedMessage(quotedText: parentMessage) .assertScrollToBottomButton(isVisible: false) .assertFile(isPresent: true) + .assertQuotedMessage(quotedText: quotedText) } WHEN("user taps on a quoted message") { - userRobot.tapOnQuotedMessage(parentMessage, at: 0) + userRobot.tapOnQuotedMessage(quotedText, at: 0) } THEN("user is scrolled up to the quoted message") { userRobot - .assertMessageIsVisible(at: messageCount) .assertScrollToBottomButton(isVisible: true) + .assertFirstMessageIsVisible(quotedText) } } - + func test_quotedReplyNotInList_whenParticipantAddsQuotedReply_Giphy() { linkToScenario(withId: 1704) - + GIVEN("user opens the channel") { - backendRobot.generateChannels(count: 1, messagesCount: messageCount) + backendRobot.generateChannels(channelsCount: 1, messagesCount: messageCount) userRobot.login().openChannel() } WHEN("participant sends giphy as a quoted reply") { - participantRobot.replyWithGiphy(toLastMessage: false) - userRobot.waitForMessageVisibility(at: 0) + participantRobot.quoteMessageWithGiphy(last: false) } THEN("user observes the quote reply in message list") { userRobot - .assertGiphyImage() - .assertQuotedMessage(quotedText: parentMessage) .assertScrollToBottomButton(isVisible: false) + .assertGiphyImage() + .assertQuotedMessage(quotedText: quotedText) } WHEN("user taps on a quoted message") { - userRobot.tapOnQuotedMessage(parentMessage, at: 0) + userRobot.tapOnQuotedMessage(quotedText, at: 0) } THEN("user is scrolled up to the quoted message") { userRobot - .assertMessageIsVisible(at: messageCount) .assertScrollToBottomButton(isVisible: true) + .assertFirstMessageIsVisible(quotedText) } } @@ -189,11 +193,11 @@ final class QuotedReply_Tests: StreamTestCase { linkToScenario(withId: 388) GIVEN("user opens the channel") { - backendRobot.generateChannels(count: 1, messagesCount: 1) + backendRobot.generateChannels(channelsCount: 1, messagesCount: 1) userRobot.login().openChannel() } AND("participant adds a quoted reply") { - participantRobot.quoteMessage(quotedMessage) + participantRobot.quoteMessage(replyText) } WHEN("participant deletes a quoted message") { participantRobot.deleteMessage() @@ -202,16 +206,39 @@ final class QuotedReply_Tests: StreamTestCase { userRobot.assertDeletedMessage() } } + + func test_originalQuoteIsDeletedByParticipant_deletedMessageIsShown() { + linkToScenario(withId: 9855) + + GIVEN("user opens the channel") { + userRobot.login().openChannel() + } + AND("participant sends a message") { + participantRobot.sendMessage("1") + } + AND("user adds a quoted reply") { + userRobot.quoteMessage(replyText) + } + WHEN("participant deletes an original message") { + participantRobot.deleteMessage() + } + THEN("deleted message is shown") { + userRobot.assertDeletedMessage(at: 1) + } + AND("deleted message is shown in quoted reply bubble") { + userRobot.assertQuotedMessage(L10n.Message.deletedMessagePlaceholder) + } + } func test_quotedReplyIsDeletedByUser_deletedMessageIsShown() { linkToScenario(withId: 389) GIVEN("user opens the channel") { - backendRobot.generateChannels(count: 1, messagesCount: 1) + backendRobot.generateChannels(channelsCount: 1, messagesCount: 1) userRobot.login().openChannel() } AND("user adds a quoted reply") { - userRobot.quoteMessage(quotedMessage) + userRobot.quoteMessage(replyText) } WHEN("user deletes a quoted message") { userRobot.deleteMessage() @@ -221,30 +248,567 @@ final class QuotedReply_Tests: StreamTestCase { } } - func test_unreadCount_whenUserSendsInvalidCommand_and_jumpingOnQuotedMessage() throws { + func test_originalQuoteIsDeletedByUser_deletedMessageIsShown() { + linkToScenario(withId: 9856) + + GIVEN("user opens the channel") { + backendRobot.generateChannels(channelsCount: 1, messagesCount: 1) + userRobot.login().openChannel() + } + AND("user adds a quoted reply") { + userRobot.quoteMessage(replyText) + } + WHEN("user deletes an original message") { + userRobot.deleteMessage(messageCellIndex: 1) + } + THEN("deleted message is shown") { + userRobot.assertDeletedMessage(at: 1) + } + AND("deleted message is shown in quoted reply bubble") { + userRobot.assertQuotedMessage(L10n.Message.deletedMessagePlaceholder) + } + } + + func test_unreadCount_whenUserSendsInvalidCommand_and_jumpingOnQuotedMessage() { linkToScenario(withId: 1705) - + let invalidCommand = "invalid command" - + GIVEN("user opens the channel") { - backendRobot.generateChannels(count: 1, messagesCount: messageCount) + backendRobot.generateChannels(channelsCount: 1, messagesCount: messageCount) userRobot.login().openChannel() } WHEN("user adds a quoted reply to participant message") { userRobot - .scrollMessageListUp(times: 4) - .quoteMessage(quotedMessage, messageCellIndex: messageCount - 1) + .scrollMessageListUp(times: 3) + .quoteMessage(replyText, messageCellIndex: messageCount - 1) + } + AND("user quotes a message with invalid command") { + userRobot.quoteMessage("/\(invalidCommand)", messageCellIndex: 0, waitForAppearance: false) } - AND("user sends a message with invalid command") { - userRobot.sendMessage("/\(invalidCommand)", waitForAppearance: false) + THEN("user observes invalid command alert") { + userRobot.assertInvalidCommand(invalidCommand) } - AND("user taps on a quoted message") { - userRobot.tapOnQuotedMessage(parentMessage, at: 0) + WHEN("user taps on a quoted message") { + userRobot.tapOnQuotedMessage(quotedText, at: 1) } - THEN("user observes error message") { + THEN("user is scrolled up to the quoted message") { userRobot .assertScrollToBottomButton(isVisible: true) .assertScrollToBottomButtonUnreadCount(0) } } + + func test_quotedReplyInList_whenUserAddsQuotedReply_InThread() { + linkToScenario(withId: 9857) + + let messageCount = 10 + let replyToMessageIndex = messageCount - 1 + + GIVEN("user opens the channel") { + backendRobot.generateChannels( + channelsCount: 1, + messagesCount: 1, + repliesCount: messageCount, + messagesText: parentText + ) + userRobot.login().openChannel() + } + WHEN("user adds a quoted reply to participant message in thread") { + userRobot + .openThread() + .scrollMessageListUp(times: 2) + .quoteMessage(replyText, messageCellIndex: replyToMessageIndex, waitForAppearance: false) + } + THEN("user observes the quote reply in thread") { + userRobot + .assertScrollToBottomButton(isVisible: false) + .assertQuotedMessage(replyText: replyText, quotedText: quotedText) + } + WHEN("user taps on a quoted message") { + userRobot.tapOnQuotedMessage(quotedText, at: 0) + } + THEN("user is scrolled up to the quoted message") { + userRobot + .assertScrollToBottomButton(isVisible: true) + .assertMessageIsVisible(at: replyToMessageIndex) + } + } + + func test_quotedReplyInList_whenParticipantAddsQuotedReply_Message_InThread() throws { + linkToScenario(withId: 9858) + + throw XCTSkip("https://linear.app/stream/issue/IOS-479") + + let messageCount = 25 + + GIVEN("user opens the channel") { + backendRobot.generateChannels( + channelsCount: 1, + messagesCount: 1, + repliesCount: messageCount, + messagesText: parentText + ) + userRobot.login().openChannel() + } + WHEN("participant adds a quoted reply") { + participantRobot.quoteMessageInThread(replyText, last: false) + } + THEN("user observes the quote reply in thread") { + userRobot + .openThread() + .assertQuotedMessage(replyText: replyText, quotedText: quotedText) + .assertScrollToBottomButton(isVisible: false) + } + WHEN("user taps on a quoted message") { + userRobot.tapOnQuotedMessage(quotedText, at: 0) + } + THEN("user is scrolled up to the quoted message") { + userRobot + .assertMessage(quotedText, at: messageCount) + .assertMessageIsVisible(at: messageCount) + .assertScrollToBottomButton(isVisible: true) + } + } + + func test_quotedReplyNotInList_whenUserAddsQuotedReply_InThread() throws { + linkToScenario(withId: 9859) + + throw XCTSkip("https://linear.app/stream/issue/IOS-479") + + let replyToMessageIndex = messageCount - 1 + + GIVEN("user opens the channel") { + backendRobot.generateChannels( + channelsCount: 1, + messagesCount: 1, + repliesCount: messageCount, + messagesText: parentText + ) + userRobot.login().openChannel() + } + WHEN("user adds a quoted reply to participant message in thread") { + userRobot + .openThread() + .scrollMessageListUp(times: 3) + .quoteMessage(replyText, messageCellIndex: replyToMessageIndex, waitForAppearance: false) + .waitForMessageVisibility(at: 0) + } + THEN("user observes the quote reply") { + userRobot + .assertQuotedMessage(replyText: replyText, quotedText: quotedText) + .assertScrollToBottomButton(isVisible: false) + } + WHEN("user taps on a quoted message") { + userRobot.tapOnQuotedMessage(quotedText, at: 0) + } + THEN("user is scrolled up to the quoted message") { + userRobot + .assertMessage(quotedText, at: messageCount) + .assertMessageIsVisible(at: messageCount) + .assertScrollToBottomButton(isVisible: true) + } + } + + func test_quotedReplyNotInList_whenParticipantAddsQuotedReply_Message_InThread() throws { + linkToScenario(withId: 9860) + + throw XCTSkip("https://linear.app/stream/issue/IOS-479") + + GIVEN("user opens the channel") { + backendRobot.generateChannels( + channelsCount: 1, + messagesCount: 1, + repliesCount: messageCount, + messagesText: parentText + ) + userRobot.login().openChannel() + } + WHEN("participant adds a quoted reply in thread") { + participantRobot.quoteMessageInThread(replyText, last: false) + } + THEN("user observes the quote reply in thread") { + userRobot + .openThread() + .assertQuotedMessage(replyText: replyText, quotedText: quotedText) + .assertScrollToBottomButton(isVisible: false) + } + WHEN("user taps on a quoted message") { + userRobot.tapOnQuotedMessage(quotedText, at: 0) + } + THEN("user is scrolled up to the quoted message") { + userRobot + .assertMessage(quotedText, at: pageSize) + .assertMessageIsVisible(at: pageSize) + .assertScrollToBottomButton(isVisible: true) + } + } + + func test_quotedReplyNotInList_whenParticipantAddsQuotedReply_File_InThread() throws { + linkToScenario(withId: 9861) + + throw XCTSkip("https://linear.app/stream/issue/IOS-479") + + GIVEN("user opens the channel") { + backendRobot.generateChannels( + channelsCount: 1, + messagesCount: 1, + repliesCount: messageCount, + messagesText: parentText + ) + userRobot.login().openChannel() + } + WHEN("participant sends file as a quoted reply in thread") { + participantRobot.quoteMessageWithAttachmentInThread(type: .file, last: false) + } + THEN("user observes the quote reply in thread") { + userRobot + .openThread() + .assertFile(isPresent: true) + .assertQuotedMessage(quotedText: quotedText) + .assertScrollToBottomButton(isVisible: false) + } + WHEN("user taps on a quoted message") { + userRobot.tapOnQuotedMessage(quotedText, at: 0) + } + THEN("user is scrolled up to the quoted message") { + userRobot + .assertFirstMessageIsVisible("1") + .assertScrollToBottomButton(isVisible: true) + } + } + + // NOTE: There used to be a problem with tapping on a Send button on iOS > 16 + func test_quotedReplyNotInList_whenParticipantAddsQuotedReply_Giphy_InThread() throws { + linkToScenario(withId: 9862) + + throw XCTSkip("https://linear.app/stream/issue/IOS-479") + + GIVEN("user opens the channel") { + backendRobot.generateChannels( + channelsCount: 1, + messagesCount: 1, + repliesCount: messageCount, + messagesText: parentText + ) + userRobot.login().openChannel() + } + WHEN("participant sends giphy as a quoted reply") { + participantRobot.quoteMessageWithGiphyInThread(last: false) + } + THEN("user observes the quote reply in thread") { + userRobot + .openThread() + .assertGiphyImage() + .assertQuotedMessage(quotedText: quotedText) + .assertScrollToBottomButton(isVisible: false) + } + WHEN("user taps on a quoted message") { + userRobot.tapOnQuotedMessage(quotedText, at: 0) + } + THEN("user is scrolled up to the quoted message") { + userRobot + .assertScrollToBottomButton(isVisible: true, timeout: 15) + .assertMessageIsVisible(at: pageSize) + } + } + + func test_unreadCount_whenUserSendsInvalidCommand_and_jumpingOnQuotedMessage_InThread() { + linkToScenario(withId: 9863) + + let invalidCommand = "invalid command" + let replyToMessageIndex = messageCount - 1 + + GIVEN("user opens the channel") { + backendRobot.generateChannels( + channelsCount: 1, + messagesCount: 1, + repliesCount: messageCount, + messagesText: parentText + ) + userRobot.login().openChannel() + } + THEN("user adds a quoted reply in thread") { + userRobot + .openThread() + .scrollMessageListUp(times: 3) + .quoteMessage(replyText, messageCellIndex: replyToMessageIndex, waitForAppearance: false) + } + AND("user quotes a message with invalid command") { + userRobot.quoteMessage("/\(invalidCommand)", messageCellIndex: 0, waitForAppearance: false) + } + THEN("user observes invalid command alert") { + userRobot.assertInvalidCommand(invalidCommand) + } + WHEN("user taps on a quoted message") { + userRobot.tapOnQuotedMessage(quotedText, at: 1) + } + THEN("user is scrolled up to the quoted message") { + userRobot + .assertScrollToBottomButton(isVisible: true) + .assertScrollToBottomButtonUnreadCount(0) + } + } + + func test_threadRepliesCount() { + linkToScenario(withId: 9864) + + let repliesCount = 5 + + GIVEN("user opens the channel") { + backendRobot.generateChannels( + channelsCount: 1, + messagesCount: 1, + repliesCount: repliesCount, + messagesText: parentText + ) + userRobot.login().openChannel() + } + THEN("user observes the number of replies in the channel") { + userRobot.assertThreadReplyCountButton(replies: repliesCount) + } + } + + func test_quotedReplyInThreadAndAlsoInChannel() { + linkToScenario(withId: 9865) + + let quotedText = String(messageCount) + + GIVEN("user opens the channel") { + backendRobot.generateChannels( + channelsCount: 1, + messagesCount: 1, + repliesCount: messageCount, + messagesText: parentText + ) + userRobot.login().openChannel() + } + WHEN("participant adds a quoted reply in thread and also in channel") { + participantRobot.quoteMessageInThread(replyText, alsoSendInChannel: true) + } + THEN("user observes the quoted reply in channel") { + userRobot + .assertQuotedMessage(replyText: replyText, quotedText: quotedText) + .assertScrollToBottomButton(isVisible: false) + } + AND("user observes the quoted reply also in thread") { + userRobot + .openThread() + .assertQuotedMessage(replyText: replyText, quotedText: quotedText) + .assertScrollToBottomButton(isVisible: false) + } + } + + func test_quotedReplyIsDeletedByParticipant_deletedMessageIsShown_InThread() { + linkToScenario(withId: 9866) + + GIVEN("user opens the channel") { + backendRobot.generateChannels( + channelsCount: 1, + messagesCount: 1, + repliesCount: 1, + messagesText: parentText + ) + userRobot.login().openChannel() + } + AND("participant adds a quoted reply") { + participantRobot.quoteMessageInThread(replyText) + } + WHEN("participant deletes a quoted message") { + participantRobot.deleteMessage() + } + THEN("user observes Message deleted in thread") { + userRobot.openThread().assertDeletedMessage() + } + } + + func test_originalQuoteIsDeletedByParticipant_deletedMessageIsShown_InThread() { + linkToScenario(withId: 9867) + + GIVEN("user opens the channel") { + backendRobot.generateChannels( + channelsCount: 1, + messagesCount: 1, + messagesText: parentText + ) + userRobot.login().openChannel() + } + AND("participant sends a message") { + participantRobot.sendMessageInThread("1") + } + AND("user adds a quoted reply") { + userRobot + .openThread(waitForThreadIcon: true) + .quoteMessage(replyText) + } + WHEN("participant deletes an original message") { + participantRobot.deleteMessage() + } + THEN("deleted message is shown") { + userRobot.assertDeletedMessage(at: 1) + } + AND("deleted message is shown in quoted reply bubble") { + userRobot.assertQuotedMessage(L10n.Message.deletedMessagePlaceholder) + } + } + + func test_quotedReplyIsDeletedByUser_deletedMessageIsShown_InThread() { + linkToScenario(withId: 9868) + + GIVEN("user opens the channel") { + backendRobot.generateChannels( + channelsCount: 1, + messagesCount: 1, + repliesCount: 1, + messagesText: parentText + ) + userRobot.login().openChannel() + } + AND("user adds a quoted reply in thread") { + userRobot + .openThread(waitForThreadIcon: true) + .quoteMessage(replyText) + } + WHEN("user deletes a quoted message") { + userRobot.deleteMessage() + } + THEN("deleted message is shown") { + userRobot.assertDeletedMessage() + } + } + + func test_originalQuoteIsDeletedByUser_deletedMessageIsShown_InThread() { + linkToScenario(withId: 9869) + + GIVEN("user opens the channel") { + backendRobot.generateChannels(channelsCount: 1, messagesCount: 1) + userRobot.login().openChannel() + } + AND("user adds a quoted reply") { + userRobot.openThread().quoteMessage(replyText) + } + WHEN("user deletes an original message") { + userRobot.deleteMessage(messageCellIndex: 1) + } + THEN("deleted message is shown") { + userRobot.assertDeletedMessage(at: 1) + } + AND("deleted message is shown in quoted reply bubble") { + userRobot.assertQuotedMessage(L10n.Message.deletedMessagePlaceholder) + } + } + + func test_rootMessageShouldOnlyBeVisibleInTheLastPageInThread() throws { + linkToScenario(withId: 9870) + + throw XCTSkip("https://linear.app/stream/issue/IOS-479") + + let replyCount = 30 + + GIVEN("user opens the channel") { + backendRobot.generateChannels( + channelsCount: 1, + messagesCount: 1, + repliesCount: replyCount, + messagesText: parentText + ) + userRobot.login().openChannel() + } + WHEN("user opens the thread with \(replyCount) replies") { + userRobot.openThread() + } + THEN("parent message is not loaded") { + userRobot.assertParentMessageInThread(withText: parentText, isLoaded: false) + } + WHEN("user scrolls up to load one more page") { + userRobot.scrollMessageListUp(times: 2) + } + THEN("parent message is loaded") { + userRobot.assertParentMessageInThread(withText: parentText, isLoaded: true) + } + } + + func test_rootMessageShouldNotBeVisibleInThreadIfMessageCountEqualToPageSize() throws { + linkToScenario(withId: 9871) + + throw XCTSkip("https://linear.app/stream/issue/IOS-479") + + let pageSize = 25 + + GIVEN("user opens the channel") { + backendRobot.generateChannels( + channelsCount: 1, + messagesCount: 1, + repliesCount: pageSize, + messagesText: parentText + ) + userRobot.login().openChannel() + } + WHEN("user opens the thread with \(pageSize) replies") { + userRobot.openThread() + } + THEN("parent message is not loaded") { + userRobot.assertParentMessageInThread(withText: parentText, isLoaded: false) + } + WHEN("user scrolls up to load one more page") { + userRobot.scrollMessageListUp(times: 2) + } + THEN("parent message is loaded") { + userRobot.assertParentMessageInThread(withText: parentText, isLoaded: true) + } + } + + func test_rootMessageShouldBeVisibleInThreadIfMessageCountLessThanPageSize() { + linkToScenario(withId: 9872) + + let messageCount = 15 + + GIVEN("user opens the channel") { + backendRobot.generateChannels( + channelsCount: 1, + messagesCount: 1, + repliesCount: messageCount, + messagesText: parentText + ) + userRobot.login().openChannel() + } + WHEN("user opens the thread with \(messageCount) replies") { + userRobot.openThread(waitForThreadIcon: true) + } + THEN("parent message is loaded") { + userRobot.assertParentMessageInThread(withText: parentText, isLoaded: true) + } + } + + func test_quoteReplyRootMessageWhenNotInTheList() throws { + linkToScenario(withId: 9873) + + throw XCTSkip("https://linear.app/stream/issue/IOS-479") + + GIVEN("user opens the thread with \(messageCount) replies") { + backendRobot.generateChannels( + channelsCount: 1, + messagesCount: 1, + repliesCount: messageCount, + messagesText: parentText + ) + userRobot.login().openChannel().openThread() + } + WHEN("user quote replies root message") { + userRobot + .scrollMessageListUp(times: 3) + .quoteMessage(replyText, messageCellIndex: messageCount, waitForAppearance: false) + } + AND("user reenters the thread") { + userRobot + .tapOnBackButton() + .openThread() + } + AND("user jumps to root message") { + userRobot.tapOnQuotedMessage(parentText) + } + THEN("parent message is loaded") { + userRobot.assertParentMessageInThread(withText: parentText, isLoaded: true) + } + } } diff --git a/StreamChatSwiftUITestsAppTests/Tests/Reactions_Tests.swift b/StreamChatSwiftUITestsAppTests/Tests/Reactions_Tests.swift index df9422970..c4cdd139e 100644 --- a/StreamChatSwiftUITestsAppTests/Tests/Reactions_Tests.swift +++ b/StreamChatSwiftUITestsAppTests/Tests/Reactions_Tests.swift @@ -5,12 +5,6 @@ import XCTest final class Reactions_Tests: StreamTestCase { - override func setUpWithError() throws { - try super.setUpWithError() - addTags([.coreFeatures]) - assertMockServer() - } - func test_addsReaction() throws { linkToScenario(withId: 270) @@ -63,7 +57,7 @@ final class Reactions_Tests: StreamTestCase { userRobot.login().openChannel() } WHEN("participant sends the message: '\(message)'") { - participantRobot.sendMessage(message, waitBeforeSending: 0.5) + participantRobot.sendMessage(message) } AND("user adds the reaction") { userRobot @@ -84,7 +78,7 @@ final class Reactions_Tests: StreamTestCase { userRobot.login().openChannel() } WHEN("participant sends the message: '\(message)'") { - participantRobot.sendMessage(message, waitBeforeSending: 0.5) + participantRobot.sendMessage(message) } AND("user adds the reaction") { userRobot @@ -154,7 +148,7 @@ final class Reactions_Tests: StreamTestCase { userRobot.login().openChannel() } WHEN("participant sends the message: '\(message)'") { - participantRobot.sendMessage(message, waitBeforeSending: 0.5) + participantRobot.sendMessage(message) } AND("participant adds the reaction") { participantRobot.addReaction(type: .wow) @@ -173,7 +167,7 @@ final class Reactions_Tests: StreamTestCase { userRobot.login().openChannel() } WHEN("participant sends the message: '\(message)'") { - participantRobot.sendMessage(message, waitBeforeSending: 0.5) + participantRobot.sendMessage(message) } AND("participant adds the reaction") { participantRobot.addReaction(type: .sad) @@ -189,8 +183,8 @@ final class Reactions_Tests: StreamTestCase { func test_addReactionWhileOffline() throws { linkToScenario(withId: 94) - - throw XCTSkip("Check out SWUI-245") + + throw XCTSkip("https://linear.app/stream/issue/IOS-1315") let message = "test message" diff --git a/StreamChatSwiftUITestsAppTests/Tests/SlowMode_Tests.swift b/StreamChatSwiftUITestsAppTests/Tests/SlowMode_Tests.swift index 8884d9b5d..17936ac0d 100644 --- a/StreamChatSwiftUITestsAppTests/Tests/SlowMode_Tests.swift +++ b/StreamChatSwiftUITestsAppTests/Tests/SlowMode_Tests.swift @@ -5,22 +5,14 @@ import XCTest final class SlowMode_Tests: StreamTestCase { - let cooldownDuration = 15 + let cooldownDuration = 5 let message = "message" let anotherNewMessage = "Another new message" let replyMessage = "reply message" let editedMessage = "edited message" - override func setUpWithError() throws { - try super.setUpWithError() - addTags([.slowMode]) - assertMockServer() - } - func test_slowModeIsActiveAndCooldownIsShown_whenNewMessageIsSent() throws { linkToScenario(withId: 450) - - throw XCTSkip("Check out issues/180") GIVEN("user opens a channel") { backendRobot.setCooldown(enabled: true, duration: cooldownDuration) @@ -29,18 +21,18 @@ final class SlowMode_Tests: StreamTestCase { .openChannel() } WHEN("user sends a new text message") { - userRobot.sendMessage(message, waitForAppearance: false) + userRobot + .assertCooldown(shouldBeVisible: false) + .sendMessage(message, waitForAppearance: false) } THEN("slow mode is active and cooldown is shown") { - userRobot.assertCooldownIsShown() + userRobot.assertCooldown(shouldBeVisible: true) } } func test_slowModeIsActiveAndCooldownIsShown_whenAMessageIsReplied() throws { linkToScenario(withId: 454) - throw XCTSkip("Check out issues/178") - GIVEN("user opens a channel") { backendRobot.setCooldown(enabled: true, duration: cooldownDuration) userRobot @@ -57,106 +49,10 @@ final class SlowMode_Tests: StreamTestCase { userRobot.sendMessage(replyMessage, waitForAppearance: false) } THEN("slow mode is active and cooldown is shown") { - userRobot.assertCooldownIsShown() + userRobot.assertCooldown(shouldBeVisible: true) } AND("message is sent") { userRobot.assertQuotedMessage(replyText: replyMessage, quotedText: message) } } - - func test_newMessageCantBeSent_whenSlowModeIsActiveAndCooldownIsShown() throws { - linkToScenario(withId: 452) - - throw XCTSkip("Check out issues/179") - - GIVEN("user opens a channel") { - backendRobot.setCooldown(enabled: true, duration: cooldownDuration) - userRobot - .login() - .openChannel() - } - AND("user sends a new text message") { - userRobot.sendMessage(message, waitForAppearance: false) - } - AND("slow mode is active and cooldown is shown") { - userRobot.assertCooldownIsShown() - } - WHEN("user tries to send a new text message") { - userRobot.attemptToSendMessageWhileInSlowMode(anotherNewMessage) - } - THEN("message is not sent") { - userRobot.assertSendButtonIsNotShown() - } - } - - func test_aMessageCantBeReplied_whenSlowModeIsActiveAndCooldownIsShown() throws { - linkToScenario(withId: 451) - - throw XCTSkip("Check out issues/179") - - GIVEN("user opens a channel") { - backendRobot.setCooldown(enabled: true, duration: cooldownDuration) - userRobot - .login() - .openChannel() - } - AND("user sends a new text message") { - userRobot.sendMessage(message) - } - AND("user selects reply to a message from context menu") { - userRobot.selectOptionFromContextMenu(option: .reply) - } - WHEN("user tries to send the reply message") { - userRobot.attemptToSendMessageWhileInSlowMode(anotherNewMessage) - } - THEN("message is not sent") { - userRobot.assertSendButtonIsNotShown() - } - } - - func test_slowModeContinuesActiveAndCooldownIsShownInThreadMessage_whenSlowModeIsActiveAndCooldownIsShownInChannel() throws { - linkToScenario(withId: 449) - - throw XCTSkip("Check out issues/179") - - GIVEN("user opens a channel") { - backendRobot.setCooldown(enabled: true, duration: cooldownDuration) - userRobot - .login() - .openChannel() - } - AND("user sends a new text message") { - userRobot.sendMessage(message) - } - AND("user selects thread to message from context menu") { - userRobot.selectOptionFromContextMenu(option: .threadReply) - } - WHEN("user tries to send the reply message") { - userRobot.attemptToSendMessageWhileInSlowMode(anotherNewMessage) - } - THEN("message is not sent") { - userRobot.assertSendButtonIsNotShown() - } - } - - func test_slowModeIsNotActiveAndCooldownIsNotShown_whenAMessageIsEdited() throws { - linkToScenario(withId: 453) - - throw XCTSkip("Check out issues/179") - - GIVEN("user opens a channel") { - backendRobot - .generateChannels(count: 1, messagesCount: 1) - .setCooldown(enabled: true, duration: cooldownDuration) - userRobot - .login() - .openChannel() - } - WHEN("user edits an existed message") { - userRobot.editMessage(editedMessage) - } - THEN("slow mode is not active and cooldown is not shown") { - userRobot.assertCooldownIsNotShown() - } - } } diff --git a/StreamChatSwiftUITestsAppTests/Tests/StreamTestCase+Tags.swift b/StreamChatSwiftUITestsAppTests/Tests/StreamTestCase+Tags.swift deleted file mode 100644 index 2a289319e..000000000 --- a/StreamChatSwiftUITestsAppTests/Tests/StreamTestCase+Tags.swift +++ /dev/null @@ -1,18 +0,0 @@ -// -// Copyright © 2025 Stream.io Inc. All rights reserved. -// - -import Foundation - -extension StreamTestCase { - enum Tags: String { - case coreFeatures = "Core Features" - case slowMode = "Slow Mode" - case offlineSupport = "Offline Support" - case messageDeliveryStatus = "Message Delivery Status" - } - - func addTags(_ tags: [Tags]) { - addTagsToScenario(tags.map { $0.rawValue }) - } -} diff --git a/fastlane/Fastfile b/fastlane/Fastfile index fe7cde1c7..71cfbc7db 100644 --- a/fastlane/Fastfile +++ b/fastlane/Fastfile @@ -15,6 +15,7 @@ github_repo = ENV['GITHUB_REPOSITORY'] || 'GetStream/stream-chat-swiftui' derived_data_path = 'derived_data' source_packages_path = 'spm_cache' is_localhost = !is_ci +mock_server_driver_port = 4568 swift_environment_path = File.absolute_path('../Sources/StreamChatSwiftUI/Generated/SystemEnvironment+Version.swift') @force_check = false @@ -27,7 +28,7 @@ before_all do |lane| end after_all do |lane| - stop_sinatra if lane == :test_e2e_mock + stop_mock_server if lane == :test_e2e_mock end lane :build_xcframeworks do @@ -272,21 +273,37 @@ lane :build_test_app_and_frameworks do ) end -desc 'Starts Sinatra web server' -lane :start_sinatra do - sh('bundle exec ruby sinatra.rb > sinatra_log.txt 2>&1 &') +lane :start_mock_server do |options| + mock_server_repo = 'stream-chat-test-mock-server' + stop_mock_server if is_localhost + + if options[:local_server] + mock_server_repo = options[:local_server] + else + branch = options[:branch].to_s.empty? ? 'main' : options[:branch] + sh("rm -rf #{mock_server_repo}") if File.directory?(mock_server_repo) + sh("git clone -b #{branch} https://github.com/#{github_repo.split('/').first}/#{mock_server_repo}.git") + end + + Dir.chdir(mock_server_repo) do + FileUtils.mkdir_p('logs') + sh("bundle exec ruby driver.rb #{mock_server_driver_port} > logs/driver.log 2>&1 &") + end end -desc 'Stops Sinatra web server' -lane :stop_sinatra do - sh('lsof -t -i:4567 | xargs kill -9') +lane :stop_mock_server do + begin + Net::HTTP.get_response(URI("http://localhost:#{mock_server_driver_port}/stop")) + rescue StandardError + nil + end end desc 'Runs e2e ui tests using mock server in Debug config' lane :test_e2e_mock do |options| next unless is_check_required(sources: sources_matrix[:e2e], force_check: @force_check) - start_sinatra + start_mock_server scan_options = { project: xcode_project, diff --git a/fastlane/sinatra.rb b/fastlane/sinatra.rb deleted file mode 100644 index dbf858c8d..000000000 --- a/fastlane/sinatra.rb +++ /dev/null @@ -1,37 +0,0 @@ -require 'sinatra' -require 'fileutils' - -post '/push/:udid/:bundle_id' do - push_data_file = 'push_payload.json' - File.write(push_data_file, request.body.read) - puts `xcrun simctl push #{params['udid']} #{params['bundle_id']} #{push_data_file}` -end - -post '/record_video/:udid/:test_name' do - recordings_dir = 'recordings' - video_base_name = "#{recordings_dir}/#{params['test_name']}" - recordings = (0..Dir["#{recordings_dir}/*"].length + 1).to_a - body = JSON.parse(request.body.read) - FileUtils.mkdir_p(recordings_dir) - - video_file = '' - if body['delete'] - recordings.reverse_each do |i| - video_file = "#{video_base_name}_#{i}.mp4" - break if File.exist?(video_file) - end - else - recordings.each do |i| - video_file = "#{video_base_name}_#{i}.mp4" - break unless File.exist?(video_file) - end - end - - if body['stop'] - simctl_processes = `pgrep simctl`.strip.split("\n") - simctl_processes.each { |pid| `kill -s SIGINT #{pid}` } - File.delete(video_file) if body['delete'] && File.exist?(video_file) - else - puts `xcrun simctl io #{params['udid']} recordVideo --codec h264 --force #{video_file} &` - end -end