Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Communication: Fix Tagging other Users does not work #71

Merged
merged 8 commits into from
Nov 14, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -417,12 +417,9 @@ internal open class ConversationViewModel(
) { authToken, serverUrl ->
retryOnInternet(networkStatusProvider.currentNetworkStatus) {
conversationService
.searchForPotentialCommunicationParticipants(
.searchForCourseMembers(
courseId = metisContext.courseId,
query = query,
includeStudents = true,
includeTutors = true,
includeInstructors = true,
authToken = authToken,
serverUrl = serverUrl
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,17 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.focus.onFocusChanged
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.unit.dp
import de.tum.informatics.www1.artemis.native_app.core.ui.markdown.MarkdownText
import de.tum.informatics.www1.artemis.native_app.feature.metis.conversation.R


const val TEST_TAG_MARKDOWN_TEXTFIELD = "TEST_TAG_MARKDOWN_TEXTFIELD"

/**
* @param sendButton composable centered vertically right to the text field.
*/
Expand Down Expand Up @@ -94,7 +98,8 @@ internal fun MarkdownTextField(
onFocusLost()
hadFocus = false
}
},
}
.testTag(TEST_TAG_MARKDOWN_TEXTFIELD),
value = textFieldValue,
onValueChange = onTextChanged,
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Text)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package de.tum.informatics.www1.artemis.native_app.feature.metis.conversation

import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.test.assertTextEquals
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onNodeWithTag
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performTextInput
import androidx.compose.ui.test.performClick
import androidx.compose.ui.text.input.TextFieldValue
import androidx.test.ext.junit.runners.AndroidJUnit4
import de.tum.informatics.www1.artemis.native_app.core.data.DataState
import de.tum.informatics.www1.artemis.native_app.feature.metis.conversation.ui.reply.ReplyTextField
import de.tum.informatics.www1.artemis.native_app.feature.metis.conversation.ui.reply.ReplyMode
import de.tum.informatics.www1.artemis.native_app.feature.metis.conversation.ui.reply.AutoCompleteCategory
import de.tum.informatics.www1.artemis.native_app.feature.metis.conversation.ui.reply.AutoCompleteHint
import de.tum.informatics.www1.artemis.native_app.feature.metis.conversation.ui.reply.LocalReplyAutoCompleteHintProvider
import de.tum.informatics.www1.artemis.native_app.feature.metis.conversation.ui.reply.ReplyAutoCompleteHintProvider
import de.tum.informatics.www1.artemis.native_app.feature.metis.conversation.ui.reply.TEST_TAG_MARKDOWN_TEXTFIELD
import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flowOf
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith

@RunWith(AndroidJUnit4::class)
class ReplyTextFieldUiTest {

@get:Rule
val composeTestRule = createComposeRule()

@Test
fun `test GIVEN an empty reply textField WHEN entering the @ character THEN a list of autocompletionHints for users shows`() {

val autoCompleteHints = listOf(
AutoCompleteCategory(R.string.markdown_textfield_autocomplete_category_users, listOf(
AutoCompleteHint("User1", "<User1>", "1"),
AutoCompleteHint("User2", "<User2>", "2"),
AutoCompleteHint("User3", "<User3>", "3")
))
)

val mockHintProvider = object : ReplyAutoCompleteHintProvider {
FelberMartin marked this conversation as resolved.
Show resolved Hide resolved
override val legalTagChars: List<Char> = listOf('@')
override fun produceAutoCompleteHints(tagChar: Char, query: String): Flow<DataState<List<AutoCompleteCategory>>> {
return flowOf(DataState.Success(autoCompleteHints))
}
}

composeTestRule.setContent {
CompositionLocalProvider(LocalReplyAutoCompleteHintProvider provides mockHintProvider) {
val text = remember { mutableStateOf(TextFieldValue()) }

ReplyTextField(
modifier = Modifier.fillMaxSize(),
replyMode = ReplyMode.NewMessage(
text,
onUpdateTextUpstream = { text.value = it }
) {
CompletableDeferred()
},
updateFailureState = {},
title = "TestChat"
)
}
}

// Click the fake TextField to expand the ReplyTextField and show the real TextField
composeTestRule.onNodeWithText("TestChat", substring = true).performClick()
composeTestRule.onNodeWithTag(TEST_TAG_MARKDOWN_TEXTFIELD).performTextInput("@")

// Verify that auto-complete hints are displayed
composeTestRule.onNodeWithText("User1").assertExists()
composeTestRule.onNodeWithText("User2").assertExists()
composeTestRule.onNodeWithText("User3").assertExists()

// Simulate clicking on an auto-complete hint
composeTestRule.onNodeWithText("User1").performClick()

// Verify that the text field contains the selected user tag
composeTestRule.onNodeWithTag(TEST_TAG_MARKDOWN_TEXTFIELD).assertTextEquals("<User1>")

// Verify that the auto-complete hints are no longer displayed
composeTestRule.onNodeWithText("User1").assertDoesNotExist()
composeTestRule.onNodeWithText("User2").assertDoesNotExist()
composeTestRule.onNodeWithText("User3").assertDoesNotExist()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,13 @@ open class ConversationServiceStub(
serverUrl: String
): NetworkResponse<List<User>> = NetworkResponse.Response(emptyList())

override suspend fun searchForCourseMembers(
courseId: Long,
query: String,
authToken: String,
serverUrl: String
): NetworkResponse<List<User>> = NetworkResponse.Response(emptyList())

override suspend fun createOneToOneConversation(
courseId: Long,
partner: String,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,13 @@ interface ConversationService {
serverUrl: String
): NetworkResponse<List<User>>

suspend fun searchForCourseMembers(
courseId: Long,
query: String,
authToken: String,
serverUrl: String
): NetworkResponse<List<User>>

suspend fun createOneToOneConversation(
courseId: Long,
partner: String,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,25 @@ class ConversationServiceImpl(private val ktorProvider: KtorProvider) : Conversa
}
}

override suspend fun searchForCourseMembers(
courseId: Long,
query: String,
authToken: String,
serverUrl: String
): NetworkResponse<List<User>> {
return performNetworkCall {
ktorProvider.ktorClient.get(serverUrl) {
url {
appendPathSegments("api", "courses", courseId.toString(), "members", "search")

parameter("loginOrName", query)
}

cookieAuth(authToken)
}.body()
}
}

override suspend fun createGroupChat(
courseId: Long,
groupMembers: List<String>,
Expand Down
Loading