From 832579bf994e3ff58c8613e5f2f568a38b3cf7a4 Mon Sep 17 00:00:00 2001 From: Martin Felber Date: Fri, 20 Dec 2024 21:14:41 +0100 Subject: [PATCH 1/7] Use IStandalonePost in ThreadUi --- .../conversation/ui/ConversationViewModel.kt | 4 ++-- .../ui/thread/ConversationThreadUseCase.kt | 9 +++++---- .../conversation/ui/thread/MetisThreadUi.kt | 11 ++++++----- .../metis/shared/content/dto/IStandalonePost.kt | 3 +++ .../metis/shared/content/dto/StandalonePost.kt | 17 +++++++++-------- .../feature/metis/shared/db/pojo/PostPojo.kt | 3 ++- 6 files changed, 27 insertions(+), 20 deletions(-) diff --git a/feature/metis/conversation/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/ui/ConversationViewModel.kt b/feature/metis/conversation/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/ui/ConversationViewModel.kt index f42284e2e..33c31eb12 100644 --- a/feature/metis/conversation/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/ui/ConversationViewModel.kt +++ b/feature/metis/conversation/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/ui/ConversationViewModel.kt @@ -398,7 +398,7 @@ internal open class ConversationViewModel( * It updates the post accordingly. */ fun toggleResolvePost( - parentPost: PostPojo, + parentPost: IStandalonePost, post: AnswerPostPojo ): Deferred { return viewModelScope.async(coroutineContext) { @@ -490,7 +490,7 @@ internal open class ConversationViewModel( } fun editAnswerPost( - parentPost: PostPojo, + parentPost: IStandalonePost, post: AnswerPostPojo, newText: String ): Deferred { diff --git a/feature/metis/conversation/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/ui/thread/ConversationThreadUseCase.kt b/feature/metis/conversation/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/ui/thread/ConversationThreadUseCase.kt index 18237fe26..5e034a98b 100644 --- a/feature/metis/conversation/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/ui/thread/ConversationThreadUseCase.kt +++ b/feature/metis/conversation/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/ui/thread/ConversationThreadUseCase.kt @@ -8,11 +8,12 @@ import de.tum.informatics.www1.artemis.native_app.core.datastore.AccountService import de.tum.informatics.www1.artemis.native_app.core.datastore.ServerConfigurationService import de.tum.informatics.www1.artemis.native_app.core.datastore.authToken import de.tum.informatics.www1.artemis.native_app.core.device.NetworkStatusProvider -import de.tum.informatics.www1.artemis.native_app.feature.metis.shared.content.MetisContext import de.tum.informatics.www1.artemis.native_app.feature.metis.conversation.service.network.MetisService import de.tum.informatics.www1.artemis.native_app.feature.metis.conversation.service.storage.MetisStorageService import de.tum.informatics.www1.artemis.native_app.feature.metis.conversation.ui.DataStatus +import de.tum.informatics.www1.artemis.native_app.feature.metis.shared.content.MetisContext import de.tum.informatics.www1.artemis.native_app.feature.metis.shared.content.StandalonePostId +import de.tum.informatics.www1.artemis.native_app.feature.metis.shared.content.dto.IStandalonePost import de.tum.informatics.www1.artemis.native_app.feature.metis.shared.content.dto.StandalonePost import de.tum.informatics.www1.artemis.native_app.feature.metis.shared.db.entities.BasePostingEntity import de.tum.informatics.www1.artemis.native_app.feature.metis.shared.db.pojo.PostPojo @@ -56,7 +57,7 @@ internal class ConversationThreadUseCase( /** * The post data state flow as loading from the server. */ - val post: StateFlow> = postId.flatMapLatest { postId -> + val post: StateFlow> = postId.flatMapLatest { postId -> when (postId) { is StandalonePostId.ClientSideId -> metisStorageService .getStandalonePost(postId.clientSideId) @@ -100,8 +101,8 @@ internal class ConversationThreadUseCase( private suspend fun handleServerLoadedStandalonePost( metisContext: MetisContext, standalonePostDataState: DataState - ): Flow> { - val failureFlow: Flow> = + ): Flow> { + val failureFlow: Flow> = flowOf(DataState.Failure(RuntimeException("Something went wrong while loading the post."))) return when (standalonePostDataState) { diff --git a/feature/metis/conversation/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/ui/thread/MetisThreadUi.kt b/feature/metis/conversation/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/ui/thread/MetisThreadUi.kt index 0c72573c5..cfa609925 100644 --- a/feature/metis/conversation/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/ui/thread/MetisThreadUi.kt +++ b/feature/metis/conversation/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/ui/thread/MetisThreadUi.kt @@ -51,6 +51,7 @@ import de.tum.informatics.www1.artemis.native_app.feature.metis.conversation.ui. 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.shared.isReplyEnabled import de.tum.informatics.www1.artemis.native_app.feature.metis.shared.content.dto.IBasePost +import de.tum.informatics.www1.artemis.native_app.feature.metis.shared.content.dto.IStandalonePost import de.tum.informatics.www1.artemis.native_app.feature.metis.shared.content.dto.conversation.Conversation import de.tum.informatics.www1.artemis.native_app.feature.metis.shared.db.pojo.AnswerPostPojo import de.tum.informatics.www1.artemis.native_app.feature.metis.shared.db.pojo.PostPojo @@ -73,7 +74,7 @@ internal fun MetisThreadUi( listContentPadding: PaddingValues, viewModel: ConversationViewModel ) { - val postDataState: DataState by viewModel.threadUseCase.post.collectAsState() + val postDataState: DataState by viewModel.threadUseCase.post.collectAsState() val clientId: Long by viewModel.clientIdOrDefault.collectAsState() val serverUrl by viewModel.serverUrl.collectAsState() @@ -154,7 +155,7 @@ internal fun MetisThreadUi( modifier: Modifier, courseId: Long, clientId: Long, - postDataState: DataState, + postDataState: DataState, conversationDataState: DataState, postActionFlags: PostActionFlags, listContentPadding: PaddingValues, @@ -254,7 +255,7 @@ internal fun MetisThreadUi( private fun PostAndRepliesList( modifier: Modifier, state: LazyListState, - post: PostPojo, + post: IStandalonePost, postActionFlags: PostActionFlags, listContentPadding: PaddingValues, clientId: Long, @@ -329,13 +330,13 @@ private fun PostAndRepliesList( itemsIndexed( post.orderedAnswerPostings, - key = { _, post -> post.postId }) { index, answerPost -> + key = { _, post -> post.clientPostId!! }) { index, answerPost -> val postActions = rememberPostActions(answerPost) PostWithBottomSheet( modifier = Modifier .fillMaxWidth() - .testTag(testTagForAnswerPost(answerPost.clientPostId)), + .testTag(testTagForAnswerPost(answerPost.clientPostId!!)), post = answerPost, postActions = postActions, postItemViewType = PostItemViewType.ThreadAnswerItem, diff --git a/feature/metis/shared/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/shared/content/dto/IStandalonePost.kt b/feature/metis/shared/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/shared/content/dto/IStandalonePost.kt index 9a1013cab..b24d2a6e0 100644 --- a/feature/metis/shared/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/shared/content/dto/IStandalonePost.kt +++ b/feature/metis/shared/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/shared/content/dto/IStandalonePost.kt @@ -15,4 +15,7 @@ interface IStandalonePost : IBasePost { val key: Any val standalonePostId: StandalonePostId? + + val orderedAnswerPostings: List + get() = answers?.sortedBy { it.creationDate } ?: emptyList() } \ No newline at end of file diff --git a/feature/metis/shared/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/shared/content/dto/StandalonePost.kt b/feature/metis/shared/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/shared/content/dto/StandalonePost.kt index be8cca95c..4f1b51282 100644 --- a/feature/metis/shared/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/shared/content/dto/StandalonePost.kt +++ b/feature/metis/shared/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/shared/content/dto/StandalonePost.kt @@ -3,7 +3,6 @@ package de.tum.informatics.www1.artemis.native_app.feature.metis.shared.content. import de.tum.informatics.www1.artemis.native_app.core.model.account.User import de.tum.informatics.www1.artemis.native_app.feature.metis.shared.content.StandalonePostId import de.tum.informatics.www1.artemis.native_app.feature.metis.shared.content.dto.conversation.Conversation -import de.tum.informatics.www1.artemis.native_app.feature.metis.shared.db.pojo.PostPojo import kotlinx.datetime.Clock import kotlinx.datetime.Instant import kotlinx.serialization.Serializable @@ -29,16 +28,18 @@ data class StandalonePost( override val resolved: Boolean? = null ) : BasePost(), IStandalonePost { - constructor(post: PostPojo, conversation: Conversation) : this( + constructor(post: IStandalonePost, conversation: Conversation) : this( id = post.serverPostId, - author = User( - id = post.authorId, - imageUrl = post.authorImageUrl - ), + author = post.authorId?.let { + User( + id = it, + imageUrl = post.authorImageUrl + ) + }, authorRole = post.authorRole, - content = post.content, + content = post.content.orEmpty(), conversation = conversation, - creationDate = post.creationDate, + creationDate = post.creationDate ?: Clock.System.now(), title = post.title, resolved = post.resolved, displayPriority = post.displayPriority diff --git a/feature/metis/shared/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/shared/db/pojo/PostPojo.kt b/feature/metis/shared/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/shared/db/pojo/PostPojo.kt index 3a28cb612..390a31492 100644 --- a/feature/metis/shared/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/shared/db/pojo/PostPojo.kt +++ b/feature/metis/shared/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/shared/db/pojo/PostPojo.kt @@ -5,6 +5,7 @@ import androidx.room.Ignore import androidx.room.Relation import de.tum.informatics.www1.artemis.native_app.feature.metis.shared.content.StandalonePostId import de.tum.informatics.www1.artemis.native_app.feature.metis.shared.content.dto.DisplayPriority +import de.tum.informatics.www1.artemis.native_app.feature.metis.shared.content.dto.IAnswerPost import de.tum.informatics.www1.artemis.native_app.feature.metis.shared.content.dto.IReaction import de.tum.informatics.www1.artemis.native_app.feature.metis.shared.content.dto.IStandalonePost import de.tum.informatics.www1.artemis.native_app.feature.metis.shared.content.dto.UserRole @@ -68,7 +69,7 @@ data class PostPojo( override val key: Any = clientPostId @Ignore - val orderedAnswerPostings = answers.sortedBy { it.creationDate } + override val orderedAnswerPostings: List = super.orderedAnswerPostings @Ignore override val standalonePostId: StandalonePostId = StandalonePostId.ClientSideId(clientPostId) From 118f769ae4ea04f92da450ead5b76f7b64cd9023 Mon Sep 17 00:00:00 2001 From: Martin Felber Date: Fri, 20 Dec 2024 21:21:47 +0100 Subject: [PATCH 2/7] Adjust tests to use new setupThreadUI signature --- .../metis/conversation/BaseChatUITest.kt | 6 +-- .../ConversationAnswerMessagesUITest.kt | 37 ++++++++++++------- .../ConversationMessagesUITest.kt | 28 ++++++++------ .../ui/post/ConversationBottomSheetUiTest.kt | 19 ++++++++++ .../ui/post/PostActionBarUITest.kt | 15 -------- .../reply/ReplyTextFieldVisibilityUITest.kt | 2 +- 6 files changed, 62 insertions(+), 45 deletions(-) diff --git a/feature/metis/conversation/src/test/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/BaseChatUITest.kt b/feature/metis/conversation/src/test/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/BaseChatUITest.kt index 8a9c2f921..0123cb295 100644 --- a/feature/metis/conversation/src/test/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/BaseChatUITest.kt +++ b/feature/metis/conversation/src/test/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/BaseChatUITest.kt @@ -78,9 +78,9 @@ abstract class BaseChatUITest : BaseComposeTest() { } fun setupThreadUi( - post: PostPojo, - onResolvePost: ((IBasePost) -> Deferred)?, - onPinPost: ((IBasePost) -> Deferred)?, + post: IStandalonePost, + onResolvePost: ((IBasePost) -> Deferred)? = { CompletableDeferred() }, + onPinPost: ((IBasePost) -> Deferred)? = { CompletableDeferred() }, hasModerationRights: Boolean = true, isAbleToPin: Boolean = true ) { diff --git a/feature/metis/conversation/src/test/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/ConversationAnswerMessagesUITest.kt b/feature/metis/conversation/src/test/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/ConversationAnswerMessagesUITest.kt index 4a87cea09..a9f4e5dae 100644 --- a/feature/metis/conversation/src/test/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/ConversationAnswerMessagesUITest.kt +++ b/feature/metis/conversation/src/test/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/ConversationAnswerMessagesUITest.kt @@ -35,10 +35,13 @@ class ConversationAnswerMessagesUITest : BaseChatUITest() { fun `test GIVEN post is not resolved WHEN resolving the post THEN the post is resolved with the first answer post`() { var resolvedPost: IBasePost? = null - setupThreadUi(post, { post -> - resolvedPost = post - CompletableDeferred() - }, { CompletableDeferred() }) + setupThreadUi( + post = post, + onResolvePost = { post -> + resolvedPost = post + CompletableDeferred() + } + ) composeTestRule.onNodeWithText(answers[0].content!!, useUnmergedTree = true) .performSemanticsAction(SemanticsActions.OnLongClick) @@ -54,10 +57,13 @@ class ConversationAnswerMessagesUITest : BaseChatUITest() { fun `test GIVEN post is not resolved WHEN resolving the post THEN the post is resolved with the third answer post`() { var resolvedPost: IBasePost? = null - setupThreadUi(post, { post -> - resolvedPost = post - CompletableDeferred() - }, { CompletableDeferred() }) + setupThreadUi( + post = post, + onResolvePost = { post -> + resolvedPost = post + CompletableDeferred() + } + ) composeTestRule.onNodeWithText(answers[2].content!!, useUnmergedTree = true) .performSemanticsAction(SemanticsActions.OnLongClick) @@ -81,10 +87,13 @@ class ConversationAnswerMessagesUITest : BaseChatUITest() { var unresolvedPost: IBasePost? = null - setupThreadUi(resolvedPost, { post -> - unresolvedPost = post - CompletableDeferred() - }, { CompletableDeferred() }) + setupThreadUi( + post = resolvedPost, + onResolvePost = { post -> + unresolvedPost = post + CompletableDeferred() + }, + ) composeTestRule.onNodeWithText(answers[resolvingIndex].content!!, useUnmergedTree = true) .performSemanticsAction(SemanticsActions.OnLongClick) @@ -98,7 +107,7 @@ class ConversationAnswerMessagesUITest : BaseChatUITest() { @Test fun `test GIVEN the post is not resolved and no answer post is resolving THEN the post is shown as not resolved and no answer post is shown as resolving`() { - setupThreadUi(post, { CompletableDeferred() }, { CompletableDeferred() }) + setupThreadUi(post) composeTestRule.onNodeWithText(post.content).assertExists() for (answer in answers) { @@ -121,7 +130,7 @@ class ConversationAnswerMessagesUITest : BaseChatUITest() { answers = modifiedAnswers ) - setupThreadUi(resolvedPost, { CompletableDeferred() }, { CompletableDeferred() }) + setupThreadUi(resolvedPost) val resolvesAssertion = hasAnyChild(hasText(context.getString(R.string.post_resolves))) diff --git a/feature/metis/conversation/src/test/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/ConversationMessagesUITest.kt b/feature/metis/conversation/src/test/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/ConversationMessagesUITest.kt index 6d68f74a9..7d9717a69 100644 --- a/feature/metis/conversation/src/test/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/ConversationMessagesUITest.kt +++ b/feature/metis/conversation/src/test/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/ConversationMessagesUITest.kt @@ -72,10 +72,13 @@ class ConversationMessagesUITest : BaseChatUITest() { fun `test GIVEN post is not pinned in thread WHEN pinning the post THEN the correct post gets pinned in thread`() { var changedPost: IBasePost? = null - setupThreadUi(posts[0], { CompletableDeferred() }, { post -> - changedPost = post - CompletableDeferred() - }) + setupThreadUi( + post = posts[0], + onPinPost = { post -> + changedPost = post + CompletableDeferred() + } + ) composeTestRule.onNodeWithTag( testTagForPost(posts[0].standalonePostId), @@ -94,10 +97,12 @@ class ConversationMessagesUITest : BaseChatUITest() { val modifiedPosts = posts.toMutableList() modifiedPosts[0] = modifiedPosts[0].copy(displayPriority = DisplayPriority.PINNED) - setupThreadUi(modifiedPosts[0], { CompletableDeferred() }, { post -> - changedPost = post - CompletableDeferred() - }) + setupThreadUi( + post = modifiedPosts[0], + onPinPost = { post -> + changedPost = post + CompletableDeferred() + }) composeTestRule.onNodeWithTag( testTagForPost(posts[0].standalonePostId), @@ -119,7 +124,7 @@ class ConversationMessagesUITest : BaseChatUITest() { @Test fun `test GIVEN the post is not pinned in thread THEN the post is not shown as pinned in thread`() { - setupThreadUi(posts[0], { CompletableDeferred() }, { CompletableDeferred() }) + setupThreadUi(posts[0]) testPinnedLabelInvisibility() } @@ -136,9 +141,8 @@ class ConversationMessagesUITest : BaseChatUITest() { @Test fun `test GIVEN the post is pinned in thread THEN the post is shown as pinned in thread`() { setupThreadUi( - posts[0].copy(displayPriority = DisplayPriority.PINNED), - { CompletableDeferred() }, - { CompletableDeferred() }) + posts[0].copy(displayPriority = DisplayPriority.PINNED) + ) testPinnedLabelVisibility() } diff --git a/feature/metis/conversation/src/test/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/ui/post/ConversationBottomSheetUiTest.kt b/feature/metis/conversation/src/test/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/ui/post/ConversationBottomSheetUiTest.kt index e4d780307..32cdfc59a 100644 --- a/feature/metis/conversation/src/test/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/ui/post/ConversationBottomSheetUiTest.kt +++ b/feature/metis/conversation/src/test/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/ui/post/ConversationBottomSheetUiTest.kt @@ -12,6 +12,7 @@ import de.tum.informatics.www1.artemis.native_app.core.model.account.User import de.tum.informatics.www1.artemis.native_app.feature.metis.conversation.BaseChatUITest import de.tum.informatics.www1.artemis.native_app.feature.metis.conversation.R import de.tum.informatics.www1.artemis.native_app.feature.metis.conversation.ui.post.post_actions.TEST_TAG_POST_CONTEXT_BOTTOM_SHEET +import de.tum.informatics.www1.artemis.native_app.feature.metis.shared.content.dto.AnswerPost import de.tum.informatics.www1.artemis.native_app.feature.metis.shared.content.dto.StandalonePost import org.junit.Test import org.junit.experimental.categories.Category @@ -99,6 +100,24 @@ class ConversationBottomSheetUiTest : BaseChatUITest() { composeTestRule.assertPostActionVisibility(R.string.post_delete, isVisible = false) } + @Test + fun `test GIVEN a basePost from another user WHEN long pressing on a users answer THEN resolve option is not shown`() { + val answerContent = "Answer content" + setupThreadUi( + post = StandalonePost( + id = 1, + author = otherUser, + content = postContent, + answers = listOf(AnswerPost( + id = 1, + author = currentUser, + content = answerContent, + )) + ), + hasModerationRights = false + ) + } + private fun ComposeTestRule.assertPostActionVisibility( stringResId: Int, isVisible: Boolean diff --git a/feature/metis/conversation/src/test/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/ui/post/PostActionBarUITest.kt b/feature/metis/conversation/src/test/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/ui/post/PostActionBarUITest.kt index c07c335ab..e141eeab7 100644 --- a/feature/metis/conversation/src/test/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/ui/post/PostActionBarUITest.kt +++ b/feature/metis/conversation/src/test/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/ui/post/PostActionBarUITest.kt @@ -9,7 +9,6 @@ import de.tum.informatics.www1.artemis.native_app.feature.metis.conversation.ui. import de.tum.informatics.www1.artemis.native_app.feature.metis.conversation.ui.post.post_actions.TEST_TAG_POST_DELETE import de.tum.informatics.www1.artemis.native_app.feature.metis.conversation.ui.post.post_actions.TEST_TAG_POST_EDIT import de.tum.informatics.www1.artemis.native_app.feature.metis.shared.content.dto.UserRole -import kotlinx.coroutines.CompletableDeferred import org.junit.Test import org.junit.experimental.categories.Category import org.junit.runner.RunWith @@ -27,8 +26,6 @@ class PostActionBarUITest: BaseChatUITest() { setupThreadUi( post = posts[0], hasModerationRights = false, - onResolvePost = { CompletableDeferred() }, - onPinPost = { CompletableDeferred() } ) composeTestRule.onNodeWithTag(TEST_TAG_POST_EDIT).assertExists().assertIsDisplayed() @@ -43,8 +40,6 @@ class PostActionBarUITest: BaseChatUITest() { authorRole = otherUserRole ), hasModerationRights = true, - onResolvePost = { CompletableDeferred() }, - onPinPost = { CompletableDeferred() } ) composeTestRule.onNodeWithTag(TEST_TAG_POST_EDIT).assertDoesNotExist() @@ -59,8 +54,6 @@ class PostActionBarUITest: BaseChatUITest() { authorRole = otherUserRole ), hasModerationRights = true, - onResolvePost = { CompletableDeferred() }, - onPinPost = { CompletableDeferred() } ) composeTestRule.onNodeWithTag(TEST_TAG_POST_DELETE).assertExists().assertIsDisplayed() @@ -70,8 +63,6 @@ class PostActionBarUITest: BaseChatUITest() { fun `test GIVEN a post WHEN navigating to the thread view as the post author THEN delete option is shown`() { setupThreadUi( post = posts[0], - onResolvePost = { CompletableDeferred() }, - onPinPost = { CompletableDeferred() } ) composeTestRule.onNodeWithTag(TEST_TAG_POST_DELETE).assertExists().assertIsDisplayed() @@ -86,8 +77,6 @@ class PostActionBarUITest: BaseChatUITest() { authorRole = otherUserRole ), hasModerationRights = false, - onResolvePost = { CompletableDeferred() }, - onPinPost = { CompletableDeferred() } ) composeTestRule.onNodeWithTag(TEST_TAG_POST_DELETE).assertDoesNotExist() @@ -102,8 +91,6 @@ class PostActionBarUITest: BaseChatUITest() { authorRole = otherUserRole ), isAbleToPin = true, - onResolvePost = { CompletableDeferred() }, - onPinPost = { CompletableDeferred() } ) composeTestRule.onNodeWithTag(TEST_TAG_PIN_POST).assertExists().assertIsDisplayed() @@ -114,8 +101,6 @@ class PostActionBarUITest: BaseChatUITest() { setupThreadUi( post = posts[0], isAbleToPin = false, - onResolvePost = { CompletableDeferred() }, - onPinPost = { CompletableDeferred() } ) composeTestRule.onNodeWithTag(TEST_TAG_PIN_POST).assertDoesNotExist() diff --git a/feature/metis/conversation/src/test/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/ui/reply/ReplyTextFieldVisibilityUITest.kt b/feature/metis/conversation/src/test/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/ui/reply/ReplyTextFieldVisibilityUITest.kt index 3b98ae120..db68dd116 100644 --- a/feature/metis/conversation/src/test/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/ui/reply/ReplyTextFieldVisibilityUITest.kt +++ b/feature/metis/conversation/src/test/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/ui/reply/ReplyTextFieldVisibilityUITest.kt @@ -18,7 +18,7 @@ class ReplyTextFieldVisibilityUITest : BaseChatUITest() { @Test fun `test GIVEN the thread view is shown containing one post and three answer posts WHEN the markdown text field is clicked THEN the keyboard is shown below the markdown text field`() { - setupThreadUi(posts[0], { CompletableDeferred() }, { CompletableDeferred() }) + setupThreadUi(posts[0]) runTest() } From 5ddcf1ba3b745fcd2adad090d6526412eee2e996 Mon Sep 17 00:00:00 2001 From: Martin Felber Date: Sat, 21 Dec 2024 10:53:26 +0100 Subject: [PATCH 3/7] Fix; adjust IAnswerPost to include parentAuthorId --- .../metis/conversation/ui/post/post_actions/PostActions.kt | 6 ++++-- .../feature/metis/conversation/ui/thread/MetisThreadUi.kt | 6 +++--- .../feature/metis/shared/content/dto/AnswerPost.kt | 3 +++ .../feature/metis/shared/content/dto/IAnswerPost.kt | 1 + .../feature/metis/shared/db/pojo/AnswerPostPojo.kt | 7 +++++++ 5 files changed, 18 insertions(+), 5 deletions(-) diff --git a/feature/metis/conversation/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/ui/post/post_actions/PostActions.kt b/feature/metis/conversation/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/ui/post/post_actions/PostActions.kt index d417e280a..cef27ce22 100644 --- a/feature/metis/conversation/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/ui/post/post_actions/PostActions.kt +++ b/feature/metis/conversation/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/ui/post/post_actions/PostActions.kt @@ -4,6 +4,7 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.remember import androidx.compose.ui.platform.LocalClipboardManager import androidx.compose.ui.text.AnnotatedString +import de.tum.informatics.www1.artemis.native_app.feature.metis.shared.content.dto.IAnswerPost import de.tum.informatics.www1.artemis.native_app.feature.metis.shared.content.dto.IBasePost data class PostActions( @@ -52,10 +53,11 @@ fun rememberPostActions( } val doesPostExistOnServer = post.serverPostId != null + val isPostAuthor = post.authorId == clientId + val isParentPostAuthor = post is IAnswerPost && post.parentAuthorId == clientId val hasResolvePostRights = - postActionFlags.isAtLeastTutorInCourse || post.authorId == clientId + postActionFlags.isAtLeastTutorInCourse || isParentPostAuthor val hasPinPostRights = postActionFlags.isAbleToPin - val isPostAuthor = post.authorId == clientId PostActions( requestEditPost = if (doesPostExistOnServer && isPostAuthor) onRequestEdit else null, diff --git a/feature/metis/conversation/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/ui/thread/MetisThreadUi.kt b/feature/metis/conversation/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/ui/thread/MetisThreadUi.kt index cfa609925..66cd5c1ba 100644 --- a/feature/metis/conversation/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/ui/thread/MetisThreadUi.kt +++ b/feature/metis/conversation/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/ui/thread/MetisThreadUi.kt @@ -63,7 +63,7 @@ import kotlinx.coroutines.Deferred import org.koin.compose.koinInject internal const val TEST_TAG_THREAD_LIST = "TEST_TAG_THREAD_LIST" -internal fun testTagForAnswerPost(answerPostId: String) = "answerPost$answerPostId" +internal fun testTagForAnswerPost(answerPostId: String?) = "answerPost$answerPostId" /** * Displays a single post with its replies. @@ -330,13 +330,13 @@ private fun PostAndRepliesList( itemsIndexed( post.orderedAnswerPostings, - key = { _, post -> post.clientPostId!! }) { index, answerPost -> + key = { index, post -> post.clientPostId ?: index }) { index, answerPost -> val postActions = rememberPostActions(answerPost) PostWithBottomSheet( modifier = Modifier .fillMaxWidth() - .testTag(testTagForAnswerPost(answerPost.clientPostId!!)), + .testTag(testTagForAnswerPost(answerPost.clientPostId)), post = answerPost, postActions = postActions, postItemViewType = PostItemViewType.ThreadAnswerItem, diff --git a/feature/metis/shared/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/shared/content/dto/AnswerPost.kt b/feature/metis/shared/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/shared/content/dto/AnswerPost.kt index 4c4d216ee..ecb8cf85a 100644 --- a/feature/metis/shared/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/shared/content/dto/AnswerPost.kt +++ b/feature/metis/shared/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/shared/content/dto/AnswerPost.kt @@ -24,6 +24,9 @@ data class AnswerPost( @Transient override val authorId: Long? = author?.id + @Transient + override val parentAuthorId: Long? = author?.id + @Transient override val serverPostId: Long? = id diff --git a/feature/metis/shared/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/shared/content/dto/IAnswerPost.kt b/feature/metis/shared/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/shared/content/dto/IAnswerPost.kt index a12a31f74..47a4c8e01 100644 --- a/feature/metis/shared/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/shared/content/dto/IAnswerPost.kt +++ b/feature/metis/shared/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/shared/content/dto/IAnswerPost.kt @@ -2,4 +2,5 @@ package de.tum.informatics.www1.artemis.native_app.feature.metis.shared.content. interface IAnswerPost : IBasePost { val resolvesPost: Boolean + val parentAuthorId: Long? } \ No newline at end of file diff --git a/feature/metis/shared/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/shared/db/pojo/AnswerPostPojo.kt b/feature/metis/shared/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/shared/db/pojo/AnswerPostPojo.kt index 34290039e..16782e7e7 100644 --- a/feature/metis/shared/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/shared/db/pojo/AnswerPostPojo.kt +++ b/feature/metis/shared/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/shared/db/pojo/AnswerPostPojo.kt @@ -18,6 +18,13 @@ data class AnswerPostPojo( val postId: String, @ColumnInfo(name = "resolves_post") override val resolvesPost: Boolean, + @Relation( + entity = BasePostingEntity::class, + entityColumn = "id", + parentColumn = "parent_post_id", + projection = ["author_id"] + ) + override val parentAuthorId: Long, @Relation( entity = BasePostingEntity::class, entityColumn = "id", From 3d1d96190bec9449ea1e0f70d664c59f67fc2a1e Mon Sep 17 00:00:00 2001 From: Martin Felber Date: Sat, 21 Dec 2024 10:53:40 +0100 Subject: [PATCH 4/7] Adjusted tests --- .../metis/conversation/BaseChatUITest.kt | 6 +- .../storage/impl/MetisStorageBaseTest.kt | 1 + .../ui/post/ConversationBottomSheetUiTest.kt | 80 ++++++++++++++++--- 3 files changed, 72 insertions(+), 15 deletions(-) diff --git a/feature/metis/conversation/src/test/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/BaseChatUITest.kt b/feature/metis/conversation/src/test/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/BaseChatUITest.kt index 0123cb295..1d40c9a97 100644 --- a/feature/metis/conversation/src/test/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/BaseChatUITest.kt +++ b/feature/metis/conversation/src/test/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/BaseChatUITest.kt @@ -37,6 +37,7 @@ abstract class BaseChatUITest : BaseComposeTest() { val answers = (0..2).map { index -> AnswerPostPojo( parentPostId = "client-id", + parentAuthorId = clientId, postId = "answer-client-id-$index", resolvesPost = false, basePostingCache = AnswerPostPojo.BasePostingCache( @@ -81,8 +82,9 @@ abstract class BaseChatUITest : BaseComposeTest() { post: IStandalonePost, onResolvePost: ((IBasePost) -> Deferred)? = { CompletableDeferred() }, onPinPost: ((IBasePost) -> Deferred)? = { CompletableDeferred() }, + isAbleToPin: Boolean = true, + isAtLeastTutorInCourse: Boolean = false, hasModerationRights: Boolean = true, - isAbleToPin: Boolean = true ) { composeTestRule.setContent { MetisThreadUi( @@ -93,7 +95,7 @@ abstract class BaseChatUITest : BaseComposeTest() { conversationDataState = DataState.Success(conversation), postActionFlags = PostActionFlags( isAbleToPin = isAbleToPin, - isAtLeastTutorInCourse = false, + isAtLeastTutorInCourse = isAtLeastTutorInCourse, hasModerationRights = hasModerationRights, ), listContentPadding = PaddingValues(), diff --git a/feature/metis/conversation/src/test/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/service/storage/impl/MetisStorageBaseTest.kt b/feature/metis/conversation/src/test/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/service/storage/impl/MetisStorageBaseTest.kt index 1ac3954ae..afc36aabc 100644 --- a/feature/metis/conversation/src/test/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/service/storage/impl/MetisStorageBaseTest.kt +++ b/feature/metis/conversation/src/test/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/service/storage/impl/MetisStorageBaseTest.kt @@ -38,6 +38,7 @@ abstract class MetisStorageBaseTest { internal val localAnswerPojo = AnswerPostPojo( parentPostId = parentClientPostId, postId = answerClientPostId, + parentAuthorId = author.id, resolvesPost = false, basePostingCache = AnswerPostPojo.BasePostingCache( serverPostId = 0, diff --git a/feature/metis/conversation/src/test/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/ui/post/ConversationBottomSheetUiTest.kt b/feature/metis/conversation/src/test/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/ui/post/ConversationBottomSheetUiTest.kt index 32cdfc59a..b0348d47f 100644 --- a/feature/metis/conversation/src/test/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/ui/post/ConversationBottomSheetUiTest.kt +++ b/feature/metis/conversation/src/test/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/ui/post/ConversationBottomSheetUiTest.kt @@ -26,6 +26,7 @@ class ConversationBottomSheetUiTest : BaseChatUITest() { private val otherUser = User(id = 1234) private val postContent = "Post content" + private val answerContent = "Answer content" @Test fun `test GIVEN a post WHEN long pressing the post THEN Edit action is shown`() { @@ -101,28 +102,81 @@ class ConversationBottomSheetUiTest : BaseChatUITest() { } @Test - fun `test GIVEN a basePost from another user WHEN long pressing on a users answer THEN resolve option is not shown`() { + fun `test GIVEN a basePost from user WHEN long pressing on another user's answer THEN resolve option is shown`() { val answerContent = "Answer content" setupThreadUi( - post = StandalonePost( - id = 1, - author = otherUser, - content = postContent, - answers = listOf(AnswerPost( - id = 1, - author = currentUser, - content = answerContent, - )) + post = simpleThreadPostWithAnswer( + postAuthor = currentUser, + answerAuthor = otherUser + ) + ) + + composeTestRule.assertPostActionVisibility( + R.string.post_resolves, + isVisible = true, + postContentToClick = answerContent + ) + } + + @Test + fun `test GIVEN a basePost from another user WHEN long pressing on another user's answer as tutor THEN resolve option is shown`() { + val answerContent = "Answer content" + setupThreadUi( + post = simpleThreadPostWithAnswer( + postAuthor = otherUser, + answerAuthor = otherUser ), - hasModerationRights = false + isAtLeastTutorInCourse = true + ) + + composeTestRule.assertPostActionVisibility( + R.string.post_resolves, + isVisible = true, + postContentToClick = answerContent + ) + } + + @Test + fun `test GIVEN a basePost from another user WHEN long pressing on a users answer THEN resolve option is not shown`() { + val answerContent = "Answer content" + setupThreadUi( + post = simpleThreadPostWithAnswer( + postAuthor = otherUser, + answerAuthor = currentUser, + ) + ) + + composeTestRule.assertPostActionVisibility( + R.string.post_resolves, + isVisible = false, + postContentToClick = answerContent + ) + } + + private fun simpleThreadPostWithAnswer( + postAuthor: User, + answerAuthor: User, + ): StandalonePost { + val basePost = StandalonePost( + id = 1, + author = postAuthor, + content = postContent, + ) + val answerPost = AnswerPost( + id = 2, + author = answerAuthor, + content = answerContent, + post = basePost ) + return basePost.copy(answers = listOf(answerPost)) } private fun ComposeTestRule.assertPostActionVisibility( stringResId: Int, - isVisible: Boolean + isVisible: Boolean, + postContentToClick: String = this@ConversationBottomSheetUiTest.postContent, ) { - onNodeWithText(postContent) + onNodeWithText(postContentToClick) .performSemanticsAction(SemanticsActions.OnLongClick) onNodeWithTag(TEST_TAG_POST_CONTEXT_BOTTOM_SHEET) From 6c6aef5d38f85fc5c7349b15f3defea8c6b4a4d3 Mon Sep 17 00:00:00 2001 From: Martin Felber Date: Sat, 21 Dec 2024 11:17:55 +0100 Subject: [PATCH 5/7] Fix compilation --- .../metis/conversation/BaseChatUITest.kt | 2 +- .../storage/impl/MetisStorageBaseTest.kt | 2 +- .../metis/shared/db/pojo/AnswerPostPojo.kt | 25 +++++++++++++------ 3 files changed, 19 insertions(+), 10 deletions(-) diff --git a/feature/metis/conversation/src/test/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/BaseChatUITest.kt b/feature/metis/conversation/src/test/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/BaseChatUITest.kt index 1d40c9a97..fb0967466 100644 --- a/feature/metis/conversation/src/test/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/BaseChatUITest.kt +++ b/feature/metis/conversation/src/test/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/BaseChatUITest.kt @@ -37,7 +37,7 @@ abstract class BaseChatUITest : BaseComposeTest() { val answers = (0..2).map { index -> AnswerPostPojo( parentPostId = "client-id", - parentAuthorId = clientId, + parentAuthorIdCache = AnswerPostPojo.ParentAuthorIdCache(clientId), postId = "answer-client-id-$index", resolvesPost = false, basePostingCache = AnswerPostPojo.BasePostingCache( diff --git a/feature/metis/conversation/src/test/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/service/storage/impl/MetisStorageBaseTest.kt b/feature/metis/conversation/src/test/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/service/storage/impl/MetisStorageBaseTest.kt index afc36aabc..09f0d57cd 100644 --- a/feature/metis/conversation/src/test/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/service/storage/impl/MetisStorageBaseTest.kt +++ b/feature/metis/conversation/src/test/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/service/storage/impl/MetisStorageBaseTest.kt @@ -38,7 +38,7 @@ abstract class MetisStorageBaseTest { internal val localAnswerPojo = AnswerPostPojo( parentPostId = parentClientPostId, postId = answerClientPostId, - parentAuthorId = author.id, + parentAuthorIdCache = AnswerPostPojo.ParentAuthorIdCache(author.id), resolvesPost = false, basePostingCache = AnswerPostPojo.BasePostingCache( serverPostId = 0, diff --git a/feature/metis/shared/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/shared/db/pojo/AnswerPostPojo.kt b/feature/metis/shared/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/shared/db/pojo/AnswerPostPojo.kt index 16782e7e7..d4f4cb959 100644 --- a/feature/metis/shared/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/shared/db/pojo/AnswerPostPojo.kt +++ b/feature/metis/shared/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/shared/db/pojo/AnswerPostPojo.kt @@ -18,13 +18,6 @@ data class AnswerPostPojo( val postId: String, @ColumnInfo(name = "resolves_post") override val resolvesPost: Boolean, - @Relation( - entity = BasePostingEntity::class, - entityColumn = "id", - parentColumn = "parent_post_id", - projection = ["author_id"] - ) - override val parentAuthorId: Long, @Relation( entity = BasePostingEntity::class, entityColumn = "id", @@ -45,7 +38,14 @@ data class AnswerPostPojo( parentColumn = "post_id", projection = ["client_post_id", "server_post_id"] ) - val serverPostIdCache: ServerPostIdCache + val serverPostIdCache: ServerPostIdCache, + @Relation( + entity = BasePostingEntity::class, + entityColumn = "id", + parentColumn = "parent_post_id", + projection = ["author_id"] + ) + val parentAuthorIdCache: ParentAuthorIdCache ) : IAnswerPost { @Ignore override val creationDate: Instant = basePostingCache.creationDate @@ -74,6 +74,9 @@ data class AnswerPostPojo( @Ignore override val clientPostId: String = postId + @Ignore + override val parentAuthorId: Long = parentAuthorIdCache.authorId + data class BasePostingCache( @ColumnInfo(name = "id") val serverPostId: Long, @@ -103,8 +106,14 @@ data class AnswerPostPojo( val authorImageUrl: String? ) + // The ids have primitive types (Long), that's why we need to add the wrapper cache class around them. data class ServerPostIdCache( @ColumnInfo(name = "server_post_id") val serverPostId: Long? ) + + data class ParentAuthorIdCache( + @ColumnInfo(name = "author_id") + val authorId: Long + ) } \ No newline at end of file From bba50650a064a27b67c5d40a5363f155ff673c65 Mon Sep 17 00:00:00 2001 From: Martin Felber Date: Sat, 21 Dec 2024 11:39:20 +0100 Subject: [PATCH 6/7] Fix implementation bug --- .../conversation/ui/post/ConversationBottomSheetUiTest.kt | 3 --- .../native_app/feature/metis/shared/content/dto/AnswerPost.kt | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/feature/metis/conversation/src/test/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/ui/post/ConversationBottomSheetUiTest.kt b/feature/metis/conversation/src/test/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/ui/post/ConversationBottomSheetUiTest.kt index b0348d47f..f3ec11891 100644 --- a/feature/metis/conversation/src/test/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/ui/post/ConversationBottomSheetUiTest.kt +++ b/feature/metis/conversation/src/test/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/ui/post/ConversationBottomSheetUiTest.kt @@ -103,7 +103,6 @@ class ConversationBottomSheetUiTest : BaseChatUITest() { @Test fun `test GIVEN a basePost from user WHEN long pressing on another user's answer THEN resolve option is shown`() { - val answerContent = "Answer content" setupThreadUi( post = simpleThreadPostWithAnswer( postAuthor = currentUser, @@ -120,7 +119,6 @@ class ConversationBottomSheetUiTest : BaseChatUITest() { @Test fun `test GIVEN a basePost from another user WHEN long pressing on another user's answer as tutor THEN resolve option is shown`() { - val answerContent = "Answer content" setupThreadUi( post = simpleThreadPostWithAnswer( postAuthor = otherUser, @@ -138,7 +136,6 @@ class ConversationBottomSheetUiTest : BaseChatUITest() { @Test fun `test GIVEN a basePost from another user WHEN long pressing on a users answer THEN resolve option is not shown`() { - val answerContent = "Answer content" setupThreadUi( post = simpleThreadPostWithAnswer( postAuthor = otherUser, diff --git a/feature/metis/shared/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/shared/content/dto/AnswerPost.kt b/feature/metis/shared/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/shared/content/dto/AnswerPost.kt index ecb8cf85a..3177f29c4 100644 --- a/feature/metis/shared/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/shared/content/dto/AnswerPost.kt +++ b/feature/metis/shared/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/shared/content/dto/AnswerPost.kt @@ -25,7 +25,7 @@ data class AnswerPost( override val authorId: Long? = author?.id @Transient - override val parentAuthorId: Long? = author?.id + override val parentAuthorId: Long? = post?.authorId @Transient override val serverPostId: Long? = id From 9b5e5617a1a06e3a68422077a1cf0c8c8de7b1c0 Mon Sep 17 00:00:00 2001 From: Martin Felber Date: Sat, 21 Dec 2024 12:07:50 +0100 Subject: [PATCH 7/7] Cleaned up tests; default all post actions rights to false --- .../metis/conversation/BaseChatUITest.kt | 10 +- .../ui/post/ConversationBottomSheetUiTest.kt | 109 ++++++++++++------ 2 files changed, 77 insertions(+), 42 deletions(-) diff --git a/feature/metis/conversation/src/test/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/BaseChatUITest.kt b/feature/metis/conversation/src/test/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/BaseChatUITest.kt index fb0967466..eb8b3d39b 100644 --- a/feature/metis/conversation/src/test/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/BaseChatUITest.kt +++ b/feature/metis/conversation/src/test/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/BaseChatUITest.kt @@ -82,9 +82,9 @@ abstract class BaseChatUITest : BaseComposeTest() { post: IStandalonePost, onResolvePost: ((IBasePost) -> Deferred)? = { CompletableDeferred() }, onPinPost: ((IBasePost) -> Deferred)? = { CompletableDeferred() }, - isAbleToPin: Boolean = true, + isAbleToPin: Boolean = false, isAtLeastTutorInCourse: Boolean = false, - hasModerationRights: Boolean = true, + hasModerationRights: Boolean = false, ) { composeTestRule.setContent { MetisThreadUi( @@ -118,6 +118,8 @@ abstract class BaseChatUITest : BaseComposeTest() { fun setupChatUi( posts: List, currentUser: User = User(id = clientId), + isAbleToPin: Boolean = false, + isAtLeastTutorInCourse: Boolean = false, hasModerationRights: Boolean = false, onPinPost: (IStandalonePost) -> Deferred = { CompletableDeferred() } ) { @@ -129,8 +131,8 @@ abstract class BaseChatUITest : BaseComposeTest() { posts = PostsDataState.Loaded.WithList(list, PostsDataState.NotLoading), clientId = currentUser.id, postActionFlags = PostActionFlags( - isAbleToPin = true, - isAtLeastTutorInCourse = false, + isAbleToPin = isAbleToPin, + isAtLeastTutorInCourse = isAtLeastTutorInCourse, hasModerationRights = hasModerationRights, ), listContentPadding = PaddingValues(), diff --git a/feature/metis/conversation/src/test/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/ui/post/ConversationBottomSheetUiTest.kt b/feature/metis/conversation/src/test/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/ui/post/ConversationBottomSheetUiTest.kt index f3ec11891..be84d472e 100644 --- a/feature/metis/conversation/src/test/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/ui/post/ConversationBottomSheetUiTest.kt +++ b/feature/metis/conversation/src/test/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/ui/post/ConversationBottomSheetUiTest.kt @@ -28,15 +28,12 @@ class ConversationBottomSheetUiTest : BaseChatUITest() { private val postContent = "Post content" private val answerContent = "Answer content" + // ###################################### EDIT ########################################### + @Test fun `test GIVEN a post WHEN long pressing the post THEN Edit action is shown`() { setupChatUi( - posts = listOf(StandalonePost( - id = 1, - author = currentUser, - content = postContent, - )), - currentUser = currentUser + posts = listOf(simplePost(currentUser)), ) composeTestRule.assertPostActionVisibility(R.string.post_edit, isVisible = true) @@ -44,29 +41,22 @@ class ConversationBottomSheetUiTest : BaseChatUITest() { @Test - fun `test GIVEN a user with moderation-rights WHEN long pressing the post THEN Edit action is not shown`() { + fun `test GIVEN a user with moderation-rights WHEN long pressing the other's post THEN Edit action is not shown`() { setupChatUi( - posts = listOf(StandalonePost( - id = 1, - author = otherUser, - content = postContent, - )), - currentUser = currentUser, + posts = listOf(simplePost(otherUser)), hasModerationRights = true ) composeTestRule.assertPostActionVisibility(R.string.post_edit, isVisible = false) } + + // ###################################### DELETE ########################################### + @Test - fun `test GIVEN a user with moderation-rights WHEN long pressing the post THEN delete option is shown`() { + fun `test GIVEN a user with moderation-rights WHEN long pressing other's post THEN delete option is shown`() { setupChatUi( - posts = listOf(StandalonePost( - id = 1, - author = otherUser, - content = postContent, - )), - currentUser = currentUser, + posts = listOf(simplePost(otherUser)), hasModerationRights = true ) @@ -76,31 +66,24 @@ class ConversationBottomSheetUiTest : BaseChatUITest() { @Test fun `test GIVEN a post WHEN long pressing the post as the post author THEN delete option is shown`() { setupChatUi( - posts = listOf(StandalonePost( - id = 1, - author = currentUser, - content = postContent, - )), - currentUser = currentUser + posts = listOf(simplePost(currentUser)), ) composeTestRule.assertPostActionVisibility(R.string.post_delete, isVisible = true) } @Test - fun `test GIVEN a post WHEN long pressing the post as non-moderator THEN delete option is not shown`() { + fun `test GIVEN a post WHEN long pressing the other's post as non-moderator THEN delete option is not shown`() { setupChatUi( - posts = listOf(StandalonePost( - id = 1, - author = otherUser, - content = postContent, - )), - currentUser = currentUser + posts = listOf(simplePost(otherUser)), ) composeTestRule.assertPostActionVisibility(R.string.post_delete, isVisible = false) } + + // ###################################### RESOLVE ########################################### + @Test fun `test GIVEN a basePost from user WHEN long pressing on another user's answer THEN resolve option is shown`() { setupThreadUi( @@ -150,15 +133,65 @@ class ConversationBottomSheetUiTest : BaseChatUITest() { ) } + + // ###################################### PIN ########################################### + + @Test + fun `test GIVEN other's post WHEN long pressing with pin rights THEN pin option is shown`() { + setupChatUi( + posts = listOf(StandalonePost( + id = 1, + author = otherUser, + content = postContent, + )), + isAbleToPin = true + ) + + composeTestRule.assertPostActionVisibility(R.string.post_pin, isVisible = true) + } + + @Test + fun `test GIVEN a post WHEN long pressing without pin rights THEN pin option is not shown`() { + setupChatUi( + posts = listOf(simplePost(otherUser)), + ) + + composeTestRule.assertPostActionVisibility(R.string.post_pin, isVisible = false) + } + + @Test + fun `test GIVEN a answer to a post WHEN long pressing the answer with pin abilities THEN pin option is not shown`() { + setupThreadUi( + post = simpleThreadPostWithAnswer( + postAuthor = currentUser, + answerAuthor = currentUser + ), + isAbleToPin = true + ) + + composeTestRule.assertPostActionVisibility( + R.string.post_pin, + isVisible = false, + postContentToClick = answerContent + ) + } + + + // ###################################### UTIL METHODS ########################################### + + private fun simplePost( + postAuthor: User, + ): StandalonePost = StandalonePost( + id = 1, + author = postAuthor, + content = postContent, + ) + private fun simpleThreadPostWithAnswer( postAuthor: User, answerAuthor: User, ): StandalonePost { - val basePost = StandalonePost( - id = 1, - author = postAuthor, - content = postContent, - ) + val basePost = simplePost(postAuthor) val answerPost = AnswerPost( id = 2, author = answerAuthor,