From b76fb9204be3ac8c51467a5ffdd3abe2f257c6e6 Mon Sep 17 00:00:00 2001 From: emmaoke-w Date: Thu, 13 Aug 2026 15:20:31 +0200 Subject: [PATCH 1/2] test: migrate connect tests (WPB-27999) --- .../tests/core/e2eTests/ConnectTests.kt | 362 ++++++++++++++++++ .../tests/core/pages/ConversationListPage.kt | 6 + .../tests/core/pages/ConversationViewPage.kt | 9 + .../core/pages/UnconnectedUserProfilePage.kt | 25 ++ 4 files changed, 402 insertions(+) create mode 100644 tests/testsCore/src/androidTest/kotlin/com/wire/android/tests/core/e2eTests/ConnectTests.kt diff --git a/tests/testsCore/src/androidTest/kotlin/com/wire/android/tests/core/e2eTests/ConnectTests.kt b/tests/testsCore/src/androidTest/kotlin/com/wire/android/tests/core/e2eTests/ConnectTests.kt new file mode 100644 index 00000000000..7bb9d71e3d4 --- /dev/null +++ b/tests/testsCore/src/androidTest/kotlin/com/wire/android/tests/core/e2eTests/ConnectTests.kt @@ -0,0 +1,362 @@ +/* + * Wire + * Copyright (C) 2026 Wire Swiss GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + */ +package com.wire.android.tests.core.e2eTests + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import backendUtils.team.updateUserProfileImage +import com.wire.android.tests.core.BaseUiTest +import com.wire.android.tests.support.UiAutomatorSetup +import com.wire.android.tests.support.tags.Category +import com.wire.android.tests.support.tags.TestCaseId +import kotlinx.coroutines.runBlocking +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import user.utils.ClientUser + +@RunWith(AndroidJUnit4::class) +class ConnectTests : BaseUiTest() { + override val deletePersonalUsersAfterTest = true + + private lateinit var personalUser: ClientUser + private lateinit var contact: ClientUser + + @Before + fun setUp() { + initCommonTestHelpers() + device = UiAutomatorSetup.start(UiAutomatorSetup.APP_ALPHA) + } + + @Suppress("CyclomaticComplexMethod", "LongMethod") + @TestCaseId("TC-4295", "TC-4298") + @Category("regression", "RC", "connect") + @Test + fun givenPersonalUser_whenSendingConnectionRequestToAnotherPersonalUser_thenConnectionRequestIsSent() { + step("Given There are personal users user1Name and user2Name") { + clientUserManager.createPersonalUsersByAliases( + listOf("user1Name", "user2Name"), + backendClient + ) + personalUser = clientUserManager.findUserByNameOrNameAlias("user1Name") + contact = clientUserManager.findUserByNameOrNameAlias("user2Name") + } + + step("And users user1Name and user2Name set their unique usernames") { + runBlocking { + backendSetupHelper.usersSetUniqueUsername("user2Name") + backendSetupHelper.usersSetUniqueUsername("user1Name") + } + } + + step("And Personal user user2Name sets profile image") { + backendClient.updateUserProfileImage(contact, context) + } + + step("And I see email verification Welcome Page") { + pages.registrationPage.assertEmailWelcomePage() + } + + step("And I open staging backend deep link") { + pages.loginPage.apply { + clickStagingDeepLink() + clickProceedButtonOnDeeplinkOverlay() + clickContinueButtonOnBackendConfigSuccess() + } + } + + step("And I enter a valid email and password to sign in") { + pages.loginPage.apply { + enterPersonalUserLoggingEmail(personalUser.email ?: "") + clickLoginButton() + assertUserLoginScreenVisible() + enterPersonalUserLoginPassword(personalUser.password ?: "") + clickLoginButton() + } + } + + step("And I wait until I am fully logged in and decline share data alert") { + pages.registrationPage.apply { + waitUntilLoginFlowIsCompleted() + clickAllowNotificationButton() + clickDeclineShareDataAlert() + } + } + + step("When I search for user2Name and open their profile") { + pages.conversationListPage.tapStartNewConversationButton() + pages.searchPage.apply { + tapSearchPeopleField() + typeUniqueUserNameInSearchField(clientUserManager, "user2Name") + assertUsernameInSearchResultIs(contact.name ?: "") + tapUsernameInSearchResult(contact.name ?: "") + } + } + + step("Then I see user2Name on the unconnected user profile page") { + pages.unconnectedUserProfilePage.assertUserNameInUnconnectedUserProfilePage(contact.name ?: "") + } + + step("When I send a connection request and return to conversation list") { + pages.unconnectedUserProfilePage.apply { + clickConnectionRequestButton() + clickCloseButtonOnUnconnectedUserProfilePage() + } + pages.searchPage.clickCloseButtonOnSearchInputField() + pages.conversationListPage.clickCloseButtonOnNewConversationScreen() + } + + step("Then I see user2Name conversation has pending status") { + pages.conversationListPage + .assertConversationNameWithPendingStatusVisibleInConversationList(contact.name ?: "") + } + + // TC-4298 - I want to verify that I cannot send a message before my contact accepts my connection request + step("When I open the pending conversation") { + pages.conversationListPage.tapConversationNameInConversationList(contact.name ?: "") + } + + step("Then I see user2Name unconnected profile and connection request information") { + pages.unconnectedUserProfilePage.apply { + assertUserNameInUnconnectedUserProfilePage(contact.name ?: "") + assertConnectionRequestInformationTextIsDisplayed( + "When your connection request is accepted, you can communicate directly with this contact." + ) + } + } + + step("And I close the unconnected user profile page") { + pages.unconnectedUserProfilePage.clickCloseButtonOnUnconnectedUserProfilePage() + } + + step("When user2Name accepts all connection requests") { + backendSetupHelper.userAcceptsAllIncomingConnectionRequests("user2Name", backendClient) + } + + step("Then I see user2Name conversation no longer has pending status") { + pages.conversationListPage.assertPendingStatusIsNoLongerVisible() + } + + step("And I see user2Name conversation and open it") { + pages.conversationListPage.apply { + assertConversationVisible(contact.name ?: "") + tapConversationNameInConversationList(contact.name ?: "") + } + } + + step("When I send the message Hello!") { + pages.conversationViewPage.apply { + typeMessageInInputField("Hello!") + clickSendButton() + } + } + + step("Then I see the message Hello! in the current conversation") { + pages.conversationViewPage.assertSentMessageIsVisibleInCurrentConversation("Hello!") + } + } + + @Suppress("CyclomaticComplexMethod", "LongMethod") + @TestCaseId("TC-4296") + @Category("regression", "RC", "connect") + @Test + fun givenPersonalUser_whenCancellingConnectionRequest_thenConnectionRequestIsCancelled() { + step("Given There are personal users user1Name and user2Name") { + clientUserManager.createPersonalUsersByAliases( + listOf("user1Name", "user2Name"), + backendClient + ) + personalUser = clientUserManager.findUserByNameOrNameAlias("user1Name") + contact = clientUserManager.findUserByNameOrNameAlias("user2Name") + } + + step("And users user1Name and user2Name set their unique usernames") { + runBlocking { + backendSetupHelper.usersSetUniqueUsername("user2Name") + backendSetupHelper.usersSetUniqueUsername("user1Name") + } + } + + step("And Personal user user2Name sets profile image") { + backendClient.updateUserProfileImage(contact, context) + } + + step("And I see email verification Welcome Page") { + pages.registrationPage.assertEmailWelcomePage() + } + + step("And I open staging backend deep link") { + pages.loginPage.apply { + clickStagingDeepLink() + clickProceedButtonOnDeeplinkOverlay() + clickContinueButtonOnBackendConfigSuccess() + } + } + + step("And I enter a valid email and password to sign in") { + pages.loginPage.apply { + enterPersonalUserLoggingEmail(personalUser.email ?: "") + clickLoginButton() + assertUserLoginScreenVisible() + enterPersonalUserLoginPassword(personalUser.password ?: "") + clickLoginButton() + } + } + + step("And I wait until I am fully logged in and decline share data alert") { + pages.registrationPage.apply { + waitUntilLoginFlowIsCompleted() + clickAllowNotificationButton() + clickDeclineShareDataAlert() + } + } + + step("When I search for user2Name and open their profile") { + pages.conversationListPage.tapStartNewConversationButton() + pages.searchPage.apply { + tapSearchPeopleField() + typeUniqueUserNameInSearchField(clientUserManager, "user2Name") + assertUsernameInSearchResultIs(contact.name ?: "") + tapUsernameInSearchResult(contact.name ?: "") + } + } + + step("Then I see user2Name on the unconnected user profile page") { + pages.unconnectedUserProfilePage.assertUserNameInUnconnectedUserProfilePage(contact.name ?: "") + } + + step("When I send the connection request") { + pages.unconnectedUserProfilePage.clickConnectionRequestButton() + } + + step("And I see the cancel connection request button and cancel the request") { + pages.unconnectedUserProfilePage.apply { + assertCancelConnectionRequestButtonVisible() + clickCancelConnectionRequestButton() + } + } + + step("And I close the unconnected profile and return to conversation list") { + pages.unconnectedUserProfilePage.clickCloseButtonOnUnconnectedUserProfilePage() + pages.searchPage.clickCloseButtonOnSearchInputField() + pages.conversationListPage.apply { + clickCloseButtonOnNewConversationScreen() + assertConversationListVisible() + } + } + + step("Then I do not see user2Name conversation") { + pages.conversationListPage.assertConversationNotVisible(contact.name ?: "") + } + } + + @Suppress("CyclomaticComplexMethod", "LongMethod") + @TestCaseId("TC-4297") + @Category("regression", "RC", "connect", "smoke", "smokeSchwarz") + @Test + fun givenPersonalUser_whenReceivingConnectionRequest_thenConnectionRequestCanBeAccepted() { + step("Given There are personal users user1Name and user2Name") { + clientUserManager.createPersonalUsersByAliases( + listOf("user1Name", "user2Name"), + backendClient + ) + personalUser = clientUserManager.findUserByNameOrNameAlias("user1Name") + contact = clientUserManager.findUserByNameOrNameAlias("user2Name") + } + + step("And users user1Name and user2Name set their unique usernames") { + runBlocking { + backendSetupHelper.usersSetUniqueUsername("user2Name") + backendSetupHelper.usersSetUniqueUsername("user1Name") + } + } + + step("And Personal user user2Name sets profile image") { + backendClient.updateUserProfileImage(contact, context) + } + + step("And I see email verification Welcome Page") { + pages.registrationPage.assertEmailWelcomePage() + } + + step("And I open staging backend deep link") { + pages.loginPage.apply { + clickStagingDeepLink() + clickProceedButtonOnDeeplinkOverlay() + clickContinueButtonOnBackendConfigSuccess() + } + } + + step("And I enter a valid email and password to sign in") { + pages.loginPage.apply { + enterPersonalUserLoggingEmail(personalUser.email ?: "") + clickLoginButton() + assertUserLoginScreenVisible() + enterPersonalUserLoginPassword(personalUser.password ?: "") + clickLoginButton() + } + } + + step("And I wait until I am fully logged in and decline share data alert") { + pages.registrationPage.apply { + waitUntilLoginFlowIsCompleted() + clickAllowNotificationButton() + clickDeclineShareDataAlert() + } + } + + step("When user2Name sends a connection request to me") { + backendSetupHelper.connectionRequestIsSentTo("user2Name", "user1Name") + } + + step("Then I see a connection request from user2Name") { + pages.conversationListPage.assertConnectionRequestNameIs(contact.name ?: "") + } + + step("And I see Wants to connect subtitle for user2Name") { + pages.conversationListPage.assertConversationSubtitleVisible("Wants to connect") + } + + step("And I open the connection request from user2Name") { + pages.conversationListPage.clickConnectionRequestOfUser(contact.name ?: "") + } + + step("And I see user2Name connection request information and actions") { + pages.unconnectedUserProfilePage.apply { + assertUserNameInUnconnectedUserProfilePage(contact.name ?: "") + assertAcceptButtonIsDisplayed() + assertIgnoreButtonIsDisplayed() + assertConnectionRequestNotificationTextIsDisplayed() + } + } + + step("When I accept the connection request") { + pages.unconnectedUserProfilePage.clickAcceptButton() + } + + step("And I see the request was accepted and start a conversation") { + pages.connectedUserProfilePage.apply { + assertToastMessageIsDisplayed("Connection request accepted") + clickStartConversationButton() + } + } + + step("Then I see conversation view with user2Name in foreground") { + pages.conversationViewPage.assertConversationIsVisibleWithUser(contact.name ?: "") + } + } +} diff --git a/tests/testsCore/src/androidTest/kotlin/com/wire/android/tests/core/pages/ConversationListPage.kt b/tests/testsCore/src/androidTest/kotlin/com/wire/android/tests/core/pages/ConversationListPage.kt index e88e0670faf..04fa7f1a3f5 100644 --- a/tests/testsCore/src/androidTest/kotlin/com/wire/android/tests/core/pages/ConversationListPage.kt +++ b/tests/testsCore/src/androidTest/kotlin/com/wire/android/tests/core/pages/ConversationListPage.kt @@ -255,6 +255,12 @@ data class ConversationListPage(private val device: UiDevice) { return this } + fun assertConversationSubtitleVisible(expectedSubtitle: String): ConversationListPage { + val subtitle = UiWaitUtils.waitElement(UiSelectorParams(text = expectedSubtitle)) + assertTrue("Conversation subtitle '$expectedSubtitle' is not visible", !subtitle.visibleBounds.isEmpty) + return this + } + fun tapSearchConversationField(): ConversationListPage { val element = UiWaitUtils.waitElement(searchField) element.click() diff --git a/tests/testsCore/src/androidTest/kotlin/com/wire/android/tests/core/pages/ConversationViewPage.kt b/tests/testsCore/src/androidTest/kotlin/com/wire/android/tests/core/pages/ConversationViewPage.kt index 462dc5101bd..baad1d5955b 100644 --- a/tests/testsCore/src/androidTest/kotlin/com/wire/android/tests/core/pages/ConversationViewPage.kt +++ b/tests/testsCore/src/androidTest/kotlin/com/wire/android/tests/core/pages/ConversationViewPage.kt @@ -128,6 +128,15 @@ data class ConversationViewPage(private val device: UiDevice) { return this } + fun assertConversationIsVisibleWithUser(userName: String): ConversationViewPage { + try { + UiWaitUtils.waitElement(displayedUserName(userName)) + } catch (e: AssertionError) { + throw AssertionError("User '$userName' is not visible in conversation view", e) + } + return this + } + fun assertConversationIsVisibleWithTeamOwner(userName: String): ConversationViewPage { try { UiWaitUtils.waitElement(displayedUserName(userName)) diff --git a/tests/testsCore/src/androidTest/kotlin/com/wire/android/tests/core/pages/UnconnectedUserProfilePage.kt b/tests/testsCore/src/androidTest/kotlin/com/wire/android/tests/core/pages/UnconnectedUserProfilePage.kt index 5f8b4e742fb..439eecdde12 100644 --- a/tests/testsCore/src/androidTest/kotlin/com/wire/android/tests/core/pages/UnconnectedUserProfilePage.kt +++ b/tests/testsCore/src/androidTest/kotlin/com/wire/android/tests/core/pages/UnconnectedUserProfilePage.kt @@ -30,6 +30,8 @@ data class UnconnectedUserProfilePage(private val device: UiDevice) { private val connectionRequestButton = UiSelectorParams(text = "Connect") + private val cancelConnectionRequestButton = UiSelectorParams(text = "Cancel Request") + private val connectionNotificationText = UiSelectorParams( textContains = "This user wants to connect with you." ) @@ -61,6 +63,12 @@ data class UnconnectedUserProfilePage(private val device: UiDevice) { return this } + fun assertConnectionRequestInformationTextIsDisplayed(expectedText: String): UnconnectedUserProfilePage { + val informationText = UiWaitUtils.waitElement(UiSelectorParams(text = expectedText)) + Assert.assertTrue("'$expectedText' text is not visible.", !informationText.visibleBounds.isEmpty) + return this + } + fun assertUserNameInUnconnectedUserProfilePage(userName: String): UnconnectedUserProfilePage { try { UiWaitUtils.waitElement(UiSelectorParams(text = userName)) @@ -79,4 +87,21 @@ data class UnconnectedUserProfilePage(private val device: UiDevice) { UiWaitUtils.waitElement(connectionRequestButton).click() return this } + + fun assertCancelConnectionRequestButtonVisible(): UnconnectedUserProfilePage { + val cancelButton = UiWaitUtils.waitElement( + cancelConnectionRequestButton, + timeout = UiWaitUtils.SHORT_WAIT + ) + Assert.assertTrue("Cancel connection request button is not visible", !cancelButton.visibleBounds.isEmpty) + return this + } + + fun clickCancelConnectionRequestButton(): UnconnectedUserProfilePage { + UiWaitUtils.waitElement( + cancelConnectionRequestButton, + timeout = UiWaitUtils.SHORT_WAIT + ).click() + return this + } } From 84aef49e2f1ad317bee6f29e14eeafde2d7e2feb Mon Sep 17 00:00:00 2001 From: emmaoke-w Date: Thu, 20 Aug 2026 12:01:48 +0200 Subject: [PATCH 2/2] test: update Calling Service Chrome versions (WPB-27999) --- tests/testsSupport/src/main/call/CallingManager.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/testsSupport/src/main/call/CallingManager.kt b/tests/testsSupport/src/main/call/CallingManager.kt index f32712d40da..0f5f5637ad9 100644 --- a/tests/testsSupport/src/main/call/CallingManager.kt +++ b/tests/testsSupport/src/main/call/CallingManager.kt @@ -69,8 +69,8 @@ class CallingManager(private val usersManager: ClientUserManager) { private val TIMEOUT_PEER_CONNECTIONS = 20.seconds private val FLOWCHECK_POLLING = 2.seconds - private const val CHROME_SUPPORT_VERSION = "102.0.5005.115" - private const val CHROME_CURRENT_VERSION = "128.0.6613.137" + private const val CHROME_SUPPORT_VERSION = "128.0.6613.137" + private const val CHROME_CURRENT_VERSION = "151.0.7922.76" } private val client = CallingServiceClient()