diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index 7b0eb99c0b..4b0661b05a 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -37,6 +37,12 @@ jobs: INTEGRATION_TESTS_USERNAME: ${{ secrets.INTEGRATION_TESTS_USERNAME }} INTEGRATION_TESTS_PASSWORD: ${{ secrets.INTEGRATION_TESTS_PASSWORD }} + - name: Check logs are set to the `trace` level + run: (grep ' TRACE ' /Users/Shared -qR) + + - name: Check logs don't contain private messages + run: (! grep 'Go down in flames' /Users/Shared -qR) + - name: Zip results # for faster upload if: failure() working-directory: fastlane/test_output diff --git a/ElementX.xcodeproj/project.pbxproj b/ElementX.xcodeproj/project.pbxproj index b29471d756..66cda910f8 100644 --- a/ElementX.xcodeproj/project.pbxproj +++ b/ElementX.xcodeproj/project.pbxproj @@ -309,6 +309,7 @@ 454311EAC17D778E19F46592 /* NotificationPermissionsScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 91868EB98818044E6FEBE532 /* NotificationPermissionsScreenCoordinator.swift */; }; 454F8DDC4442C0DE54094902 /* LABiometryType.swift in Sources */ = {isa = PBXBuildFile; fileRef = D3F219838588C62198E726E3 /* LABiometryType.swift */; }; 4557192F5B15A8D9BB920232 /* AdvancedSettingsScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E492690C8B27A892C194CC4 /* AdvancedSettingsScreenCoordinator.swift */; }; + 45D6DC594816288983627484 /* UITestsScreenIdentifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CEBE5EA91E8691EDF364EC2 /* UITestsScreenIdentifier.swift */; }; 46562110EE202E580A5FFD9C /* RoomScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 93CF7B19FFCF8EFBE0A8696A /* RoomScreenViewModelTests.swift */; }; 4681820102DAC8BA586357D4 /* VoiceMessageCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = DAB8D7926A5684E18196B538 /* VoiceMessageCache.swift */; }; 46A183C6125A669AEB005699 /* UserProfileScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = F134D2D91DFF732FB75B2CB7 /* UserProfileScreenViewModelProtocol.swift */; }; @@ -456,6 +457,7 @@ 663E198678778F7426A9B27D /* Collection.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9FAFE1C2149E6AC8156ED2B /* Collection.swift */; }; 67160204A8D362BB7D4AD259 /* Search.swift in Sources */ = {isa = PBXBuildFile; fileRef = 693E16574C6F7F9FA1015A8C /* Search.swift */; }; 6786C4B0936AC84D993B20BF /* NotificationSettingsScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = C5F06F2F09B2EDD067DC2174 /* NotificationSettingsScreen.swift */; }; + 6793E75E3EBE48EBB8F857AF /* ProcessInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 077B01C13BBA2996272C5FB5 /* ProcessInfo.swift */; }; 67C05C50AD734283374605E3 /* MatrixEntityRegex.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6AD1A853D605C2146B0DC028 /* MatrixEntityRegex.swift */; }; 67D6E0700A9C1E676F6231F8 /* Collections in Frameworks */ = {isa = PBXBuildFile; productRef = AD544C0FA48DFFB080920061 /* Collections */; }; 67E9926C4572C54F59FCA91A /* AuthenticationFlowCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9B069D7772DDF6513E0F1B8 /* AuthenticationFlowCoordinator.swift */; }; @@ -523,6 +525,7 @@ 7640A4B412CACF15D143CCD4 /* Strings+SAS.swift in Sources */ = {isa = PBXBuildFile; fileRef = B172057567E049007A5C4D92 /* Strings+SAS.swift */; }; 767D366C40F1311CFA333763 /* PillContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 86376BEE425704AEE197CA54 /* PillContext.swift */; }; 7691233E3572A9173FD96CB3 /* SecureBackupKeyBackupScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2E88534A39781D76487D59DF /* SecureBackupKeyBackupScreenViewModelTests.swift */; }; + 76C874243A8C440D6CF7B344 /* ProcessInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 077B01C13BBA2996272C5FB5 /* ProcessInfo.swift */; }; 7708976CEE6AFB5CFAEFBA68 /* PillTextAttachment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9CF1EE0AA78470C674554262 /* PillTextAttachment.swift */; }; 7756C4E90CABE6F14F7920A0 /* BugReportUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6FEA87EA3752203065ECE27 /* BugReportUITests.swift */; }; 77574A519A4E484880053EAD /* IdentityConfirmationScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7FDF541AE914059942B575B4 /* IdentityConfirmationScreenModels.swift */; }; @@ -1126,7 +1129,6 @@ FCDA202B246F75BA28E10C5F /* MapTilerAuthorization.swift in Sources */ = {isa = PBXBuildFile; fileRef = E062C1750EFC8627DE4CAB8E /* MapTilerAuthorization.swift */; }; FD29471C72872F8B7580E3E1 /* KeychainControllerMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39C0D861FC397AC34BCF089E /* KeychainControllerMock.swift */; }; FD4C21F8DA1E273DE94FCD1A /* NotificationItemProxyProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1B927CF5EF7FCCDA5EDC474B /* NotificationItemProxyProtocol.swift */; }; - FD4DEC88210F35C35B2FB386 /* ProcessInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3A1398EFF65090FDA1CB639 /* ProcessInfo.swift */; }; FD762761C5D0C30E6255C3D8 /* ServerConfirmationScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABA4CF2F5B4F68D02E412004 /* ServerConfirmationScreenViewModelProtocol.swift */; }; FDD5B4B616D9FF4DE3E9A418 /* QRCodeScannerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 92DB574F954CC2B40F7BE892 /* QRCodeScannerView.swift */; }; FE4593FC2A02AAF92E089565 /* ElementAnimations.swift in Sources */ = {isa = PBXBuildFile; fileRef = EF1593DD87F974F8509BB619 /* ElementAnimations.swift */; }; @@ -1229,6 +1231,7 @@ 06FAE373A7F20780BA84B59C /* MessageForwardingScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageForwardingScreenCoordinator.swift; sourceTree = ""; }; 0724EBDFE8BB4C9E5547C57D /* Client.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Client.swift; sourceTree = ""; }; 07579F9C29001E40715F3014 /* NotificationSettingsChatType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationSettingsChatType.swift; sourceTree = ""; }; + 077B01C13BBA2996272C5FB5 /* ProcessInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProcessInfo.swift; sourceTree = ""; }; 077D7C3BE199B6E5DDEC07EC /* AppCoordinatorStateMachine.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppCoordinatorStateMachine.swift; sourceTree = ""; }; 07C6B0B087FE6601C3F77816 /* JoinedRoomProxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JoinedRoomProxy.swift; sourceTree = ""; }; 0825EAFD47332DD459DE893F /* SessionDirectoriesTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionDirectoriesTests.swift; sourceTree = ""; }; @@ -1967,7 +1970,6 @@ B2EAFFD44F81F86012D6EC27 /* AudioRoomTimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AudioRoomTimelineView.swift; sourceTree = ""; }; B3005886F00029F058DB62BE /* StartChatScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StartChatScreenCoordinator.swift; sourceTree = ""; }; B383DCD3DCB19E00FD478A5F /* ConfirmationDialog.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfirmationDialog.swift; sourceTree = ""; }; - B3A1398EFF65090FDA1CB639 /* ProcessInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProcessInfo.swift; sourceTree = ""; }; B4005D82E9D27BAF006A8FE1 /* AppLockScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppLockScreenViewModel.swift; sourceTree = ""; }; B40233F2989AD49906BB310D /* RoomPollsHistoryScreenViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomPollsHistoryScreenViewModelTests.swift; sourceTree = ""; }; B410B32B72C90BF94E481F33 /* AppLockSetupPINScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppLockSetupPINScreenModels.swift; sourceTree = ""; }; @@ -3249,6 +3251,7 @@ 95BAC0F6C9644336E9567EE6 /* NSRegularExpresion.swift */, 62B07B296D7A9D2F09120853 /* OrderedSet.swift */, D26813CCE39221FE30BF22CD /* PlatformViewVersionPredicate.swift */, + 077B01C13BBA2996272C5FB5 /* ProcessInfo.swift */, 1DFE0E493FB55E5A62E7852A /* ProposedViewSize.swift */, 7310D8DFE01AF45F0689C3AA /* Publisher.swift */, 584A61D9C459FAFEF038A7C0 /* Section.swift */, @@ -4855,7 +4858,6 @@ 7B25F959A434BB9923A3223F /* ExpiringTaskRunner.swift */, 6A580295A56B55A856CC4084 /* InfoPlistReader.swift */, 6AD1A853D605C2146B0DC028 /* MatrixEntityRegex.swift */, - B3A1398EFF65090FDA1CB639 /* ProcessInfo.swift */, 53482ECA4B6633961EC224F5 /* ScrollViewAdapter.swift */, 4481799F455B3DA243BDA2AC /* ShareToMapsAppActivity.swift */, B1E227F34BE43B08E098796E /* TestablePreview.swift */, @@ -6033,6 +6035,7 @@ 62418EA4E3EB597AD184AEB6 /* PillConstants.swift in Sources */, 55CDD3968D95D1A820B5491E /* PlaceholderAvatarImage.swift in Sources */, F12F6BED7B6D7EE4BEE55039 /* PlainMentionBuilder.swift in Sources */, + 76C874243A8C440D6CF7B344 /* ProcessInfo.swift in Sources */, 414F50CFCFEEE2611127DCFB /* RestorationToken.swift in Sources */, 17BC15DA08A52587466698C5 /* RoomMessageEventStringBuilder.swift in Sources */, 7354D094A4C59B555F407FA1 /* RustTracing.swift in Sources */, @@ -6043,6 +6046,7 @@ E0FB26262689F04D66A949D7 /* TestablePreview.swift in Sources */, 24DF253C18D3E2C56DD0E597 /* TracingConfiguration.swift in Sources */, DDB47D29C6865669288BF87C /* UIFont+AttributedStringBuilder.m in Sources */, + 45D6DC594816288983627484 /* UITestsScreenIdentifier.swift in Sources */, 281BED345D59A9A6A99E9D98 /* UNNotificationContent.swift in Sources */, 518C93DC6516D3D018DE065F /* UNNotificationRequest.swift in Sources */, 06B55882911B4BF5B14E9851 /* URL.swift in Sources */, @@ -6648,7 +6652,7 @@ 128FFD8A3D85845F9A927F47 /* PollRoomTimelineView.swift in Sources */, 1307268DC41730E5BCF7D9A0 /* PollView.swift in Sources */, DF504B10A4918F971A57BEF2 /* PostHogAnalyticsClient.swift in Sources */, - FD4DEC88210F35C35B2FB386 /* ProcessInfo.swift in Sources */, + 6793E75E3EBE48EBB8F857AF /* ProcessInfo.swift in Sources */, 69DE29C3E3180BB17D840690 /* ProgressCursorModifier.swift in Sources */, C7ABEBECDC513F7887DACF66 /* ProgressMaskModifier.swift in Sources */, 9B356742E035D90A8BB5CABE /* ProposedViewSize.swift in Sources */, diff --git a/ElementX/Sources/Other/AccessibilityIdentifiers.swift b/ElementX/Sources/Other/AccessibilityIdentifiers.swift index e78721e64a..df2c1d07d6 100644 --- a/ElementX/Sources/Other/AccessibilityIdentifiers.swift +++ b/ElementX/Sources/Other/AccessibilityIdentifiers.swift @@ -126,6 +126,9 @@ enum A11yIdentifiers { let timelineItemActionMenu = "room-timeline_item_action_menu" let joinCall = "room-join_call" let scrollToBottom = "room-scroll_to_bottom" + + let messageComposer = "room-message_composer" + let sendButton = "room-send_button" let composerToolbar = ComposerToolbar() diff --git a/ElementX/Sources/Other/ProcessInfo.swift b/ElementX/Sources/Other/Extensions/ProcessInfo.swift similarity index 100% rename from ElementX/Sources/Other/ProcessInfo.swift rename to ElementX/Sources/Other/Extensions/ProcessInfo.swift diff --git a/ElementX/Sources/Other/Logging/RustTracing.swift b/ElementX/Sources/Other/Logging/RustTracing.swift index 243d2d9e81..8e4c0b9b32 100644 --- a/ElementX/Sources/Other/Logging/RustTracing.swift +++ b/ElementX/Sources/Other/Logging/RustTracing.swift @@ -13,7 +13,13 @@ enum RustTracing { /// name and other log management metadata during rotation. static let filePrefix = "console" /// The directory that stores all of the log files. - static var logsDirectory: URL { .appGroupContainerDirectory } + static var logsDirectory: URL { + if ProcessInfo.isRunningIntegrationTests { + "/Users/Shared" + } else { + .appGroupContainerDirectory + } + } private(set) static var currentTracingConfiguration: TracingConfiguration? static func setup(configuration: TracingConfiguration) { @@ -23,7 +29,15 @@ enum RustTracing { // as the app is unlikely to be running continuously. let maxFiles: UInt64 = 24 * 7 - setupTracing(config: .init(filter: configuration.filter, + // Log everything on integration tests to check whether + // the logs contain any sensitive data. See `UserFlowTests.swift` + let filter = if ProcessInfo.isRunningIntegrationTests { + TracingConfiguration(logLevel: .trace, target: nil).filter + } else { + configuration.filter + } + + setupTracing(config: .init(filter: filter, writeToStdoutOrSystem: true, writeToFiles: .init(path: logsDirectory.path(percentEncoded: false), filePrefix: configuration.fileName, diff --git a/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/View/ComposerToolbar.swift b/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/View/ComposerToolbar.swift index 4f20be1a38..bf61858663 100644 --- a/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/View/ComposerToolbar.swift +++ b/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/View/ComposerToolbar.swift @@ -143,6 +143,7 @@ struct ComposerToolbar: View { .disabled(context.viewState.sendButtonDisabled) .animation(.linear(duration: 0.1).disabledDuringTests(), value: context.viewState.sendButtonDisabled) .keyboardShortcut(.return, modifiers: [.command]) + .accessibilityIdentifier(A11yIdentifiers.roomScreen.sendButton) } private var messageComposer: some View { @@ -174,6 +175,7 @@ struct ComposerToolbar: View { .focused($composerFocused) .padding(.leading, context.composerFormattingEnabled ? 7 : 0) .padding(.trailing, context.composerFormattingEnabled ? 4 : 0) + .accessibilityIdentifier(A11yIdentifiers.roomScreen.messageComposer) .onTapGesture { guard !composerFocused else { return } composerFocused = true diff --git a/IntegrationTests/Sources/UserFlowTests.swift b/IntegrationTests/Sources/UserFlowTests.swift index 46e4e0f085..a5f78038d1 100644 --- a/IntegrationTests/Sources/UserFlowTests.swift +++ b/IntegrationTests/Sources/UserFlowTests.swift @@ -8,6 +8,9 @@ import XCTest class UserFlowTests: XCTestCase { + private static let integrationTestsRoomName = "Element X iOS Integration Tests" + private static let integrationTestsMessage = "Go down in flames!" + private var app: XCUIApplication! override func setUp() { @@ -16,15 +19,28 @@ class UserFlowTests: XCTestCase { } func testUserFlow() { + checkRoomFlows() + checkSettings() checkRoomCreation() - // Open the first room in the list. - let firstRoom = app.buttons.matching(NSPredicate(format: "identifier BEGINSWITH %@", A11yIdentifiers.homeScreen.roomNamePrefix)).firstMatch + app.logout() + } + + // Assumes app is on the home screen + private func checkRoomFlows() { + // Search for the special test room + let searchField = app.searchFields.firstMatch + searchField.clearAndTypeText(Self.integrationTestsRoomName) + + // And open it + let firstRoom = app.buttons.matching(NSPredicate(format: "identifier CONTAINS %@", Self.integrationTestsRoomName)).firstMatch XCTAssertTrue(firstRoom.waitForExistence(timeout: 10.0)) firstRoom.tap() + sendMessages() + checkPhotoSharing() checkDocumentSharing() @@ -35,21 +51,58 @@ class UserFlowTests: XCTestCase { checkRoomDetails() - app.logout() + // Go back to the room list + tapOnBackButton("Chats") + + // Cancel initial the room search + let searchCancelButton = app.buttons["Cancel"].firstMatch + XCTAssertTrue(searchCancelButton.waitForExistence(timeout: 10.0)) + searchCancelButton.forceTap() + } + + private func sendMessages() { + var composerTextField = app.textViews[A11yIdentifiers.roomScreen.messageComposer].firstMatch + XCTAssertTrue(composerTextField.waitForExistence(timeout: 10.0)) + composerTextField.clearAndTypeText(Self.integrationTestsMessage) + + var sendButton = app.buttons[A11yIdentifiers.roomScreen.sendButton].firstMatch + XCTAssertTrue(sendButton.waitForExistence(timeout: 10.0)) + sendButton.tap() + + sleep(10) // Wait for the message to be sent + + // Switch to the rich text editor + tapOnMenu(A11yIdentifiers.roomScreen.composerToolbar.openComposeOptions) + tapOnButton(A11yIdentifiers.roomScreen.attachmentPickerTextFormatting) + + composerTextField = app.textViews[A11yIdentifiers.roomScreen.messageComposer].firstMatch + XCTAssertTrue(composerTextField.waitForExistence(timeout: 10.0)) + composerTextField.clearAndTypeText(Self.integrationTestsMessage) + + sendButton = app.buttons[A11yIdentifiers.roomScreen.sendButton].firstMatch + XCTAssertTrue(sendButton.waitForExistence(timeout: 10.0)) + sendButton.tap() + + sleep(5) // Wait for the message to be sent + + // Close the formatting options + app.buttons[A11yIdentifiers.roomScreen.composerToolbar.closeFormattingOptions].tap() } private func checkPhotoSharing() { - // Open attachments picker tapOnMenu(A11yIdentifiers.roomScreen.composerToolbar.openComposeOptions) - - // Open photo library picker tapOnButton(A11yIdentifiers.roomScreen.attachmentPickerPhotoLibrary) + sleep(10) // Wait for the picker to load + // Tap on the second image. First one is always broken on simulators. let secondImage = app.scrollViews.images.element(boundBy: 1) - XCTAssertTrue(secondImage.waitForExistence(timeout: 10.0)) // Photo library takes a bit to load + XCTAssertTrue(secondImage.waitForExistence(timeout: 20.0)) // Photo library takes a bit to load secondImage.tap() + // Wait for the image to be processed and the new screen to appear + sleep(10) + // Cancel the upload flow tapOnButton("Cancel", waitForDisappearance: true) } @@ -58,6 +111,8 @@ class UserFlowTests: XCTestCase { tapOnMenu(A11yIdentifiers.roomScreen.composerToolbar.openComposeOptions) tapOnButton(A11yIdentifiers.roomScreen.attachmentPickerDocuments) + sleep(10) // Wait for the picker to load + tapOnButton("Cancel", waitForDisappearance: true) } @@ -65,6 +120,8 @@ class UserFlowTests: XCTestCase { tapOnMenu(A11yIdentifiers.roomScreen.composerToolbar.openComposeOptions) tapOnButton(A11yIdentifiers.roomScreen.attachmentPickerLocation) + sleep(10) // Wait for the picker to load + // The order of the alerts is a bit of a mistery so try twice allowLocationPermissionOnce() @@ -139,9 +196,6 @@ class UserFlowTests: XCTestCase { // Go back to the room tapOnBackButton("Chat") - - // Go back to the room list - tapOnBackButton("Chats") } private func checkSettings() { diff --git a/NSE/SupportingFiles/target.yml b/NSE/SupportingFiles/target.yml index fae28e976b..417469aa13 100644 --- a/NSE/SupportingFiles/target.yml +++ b/NSE/SupportingFiles/target.yml @@ -87,6 +87,7 @@ targets: - path: ../../ElementX/Sources/Other/Extensions/ImageCache.swift - path: ../../ElementX/Sources/Other/Extensions/LayoutDirection.swift - path: ../../ElementX/Sources/Other/Extensions/NSRegularExpresion.swift + - path: ../../ElementX/Sources/Other/Extensions/ProcessInfo.swift - path: ../../ElementX/Sources/Other/Extensions/String.swift - path: ../../ElementX/Sources/Other/Extensions/Task.swift - path: ../../ElementX/Sources/Other/Extensions/UNNotificationContent.swift @@ -112,3 +113,4 @@ targets: - path: ../../ElementX/Sources/Services/Room/RoomSummary/RoomMessageEventStringBuilder.swift - path: ../../ElementX/Sources/Services/UserSession/RestorationToken.swift - path: ../../ElementX/Sources/Services/UserSession/SessionDirectories.swift + - path: ../../ElementX/Sources/UITests/UITestsScreenIdentifier.swift