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

Viewmodel : members management #400

Merged
merged 15 commits into from
Jun 2, 2024
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
package com.github.se.assocify.screens.profile

import android.util.Log
import androidx.compose.ui.test.assertCountEquals
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.assertIsSelected
import androidx.compose.ui.test.assertTextContains
import androidx.compose.ui.test.hasTestTag
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onAllNodesWithTag
import androidx.compose.ui.test.onNodeWithTag
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performClick
Expand All @@ -14,6 +14,7 @@ import androidx.compose.ui.test.performScrollToNode
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.github.se.assocify.model.CurrentUser
import com.github.se.assocify.model.database.AssociationAPI
import com.github.se.assocify.model.database.UserAPI
import com.github.se.assocify.model.entities.Association
import com.github.se.assocify.model.entities.AssociationMember
import com.github.se.assocify.model.entities.PermissionRole
Expand Down Expand Up @@ -41,7 +42,7 @@ class ProfileMembersScreenTest :
private val navActions = mockk<NavigationActions>()
private var goBack = false

private val userList =
private var userList =
listOf(
User("1", "Sarah"),
User("2", "veryveryveryverylooooooooooooooooooooongnameeeeeeeeee"),
Expand All @@ -56,14 +57,29 @@ class ProfileMembersScreenTest :
User("11", "Ivy"),
)

private var userRole: MutableMap<String, RoleType> =
mutableMapOf(
"1" to RoleType.PRESIDENCY,
"2" to RoleType.TREASURY,
"3" to RoleType.MEMBER,
"4" to RoleType.MEMBER,
"5" to RoleType.MEMBER,
"6" to RoleType.MEMBER,
"7" to RoleType.MEMBER,
"8" to RoleType.MEMBER,
"9" to RoleType.MEMBER,
"10" to RoleType.MEMBER,
"11" to RoleType.MEMBER,
)

private val applicantList = userList.take(2)

private val assoMembers: List<AssociationMember> =
private var assoMembers: List<AssociationMember> =
userList.map {
AssociationMember(
it,
Association("a", "assoName", "", LocalDate.EPOCH),
PermissionRole("r", "a", RoleType.MEMBER))
PermissionRole("r", "a", userRole[it.uid]!!))
}

private val associationAPI =
Expand All @@ -76,6 +92,27 @@ class ProfileMembersScreenTest :
{
secondArg<(List<AssociationMember>) -> Unit>().invoke(assoMembers)
}
every { updateCache(any(), any()) } answers
{
firstArg<(Map<String, Association>) -> Unit>()
.invoke(mapOf("a" to Association("a", "assoName", "", LocalDate.EPOCH)))
}
}

private val userAPI =
mockk<UserAPI> {
every { removeUserFromAssociation(any(), any(), any(), any()) } answers
{
assoMembers = assoMembers.filter { it.user.uid != firstArg<String>() }
val onSuccess = thirdArg<() -> Unit>()
onSuccess()
}
every { changeRoleOfUser(any(), any(), any(), any(), any()) } answers
{
userRole[firstArg<String>()] = thirdArg<RoleType>()
val onSuccess = arg<() -> Unit>(3)
onSuccess()
}
}

@Before
Expand All @@ -87,7 +124,7 @@ class ProfileMembersScreenTest :

composeTestRule.setContent {
ProfileMembersScreen(
navActions = navActions, ProfileMembersViewModel(navActions, associationAPI))
navActions = navActions, ProfileMembersViewModel(associationAPI, userAPI))
}
}

Expand All @@ -96,11 +133,6 @@ class ProfileMembersScreenTest :
with(composeTestRule) {
onNodeWithTag("Members Screen").assertIsDisplayed()

onNodeWithText("New requests").assertIsDisplayed()
applicantList.forEach { onNodeWithTag("applicantCard-${it.uid}").assertIsDisplayed() }
onAllNodesWithTag("rejectButton").assertCountEquals(applicantList.size)
onAllNodesWithTag("acceptButton").assertCountEquals(applicantList.size)

onNodeWithText("Current members").performScrollTo().assertIsDisplayed()
assoMembers.forEach {
Log.e("assoMembers", it.user.uid)
Expand All @@ -110,6 +142,43 @@ class ProfileMembersScreenTest :
}
}

@Test
fun editMember() {
with(composeTestRule) {
val member = assoMembers[0]
val originalRole = member.role.type.name
onNodeWithTag("memberItem-${member.user.uid}").assertTextContains(originalRole)
onNodeWithTag("editButton-0").performClick()
onNodeWithText("Change ${member.user.name}'s role ?").assertIsDisplayed()
onNodeWithTag("role-${originalRole}").assertIsSelected()
onNodeWithTag("role-${RoleType.PRESIDENCY.name}").performClick()
onNodeWithTag("confirmButton").performClick()
onNodeWithTag("memberItem-${member.user.uid}").assertTextContains(RoleType.PRESIDENCY.name)
onNodeWithTag("editButton-0").performClick()
onNodeWithTag("role-${RoleType.PRESIDENCY.name}").assertIsSelected()
onNodeWithTag("role-${originalRole}").performClick()
onNodeWithTag("cancelButton").performClick()
onNodeWithTag("memberItem-${member.user.uid}").assertTextContains(RoleType.PRESIDENCY.name)
}
}

@Test
fun deleteMember() {
with(composeTestRule) {
val member = assoMembers[1]
onNodeWithTag("memberItem-${assoMembers[0].user.uid}").assertIsDisplayed()
onNodeWithTag("deleteMemberButton-0").performClick()
onNodeWithText("You cannot remove yourself").assertIsDisplayed()
onNodeWithTag("deleteMemberButton-1").performClick()
onNodeWithText("Are you sure you want to remove ${member.user.name} from the association?")
.assertIsDisplayed()
onNodeWithTag("cancelButton").performClick()
onNodeWithTag("deleteMemberButton-1").performClick()
onNodeWithTag("confirmButton").performClick()
onNodeWithTag("memberItem-${member.user.uid}").assertDoesNotExist()
}
}

@Test
fun goBack() {
with(composeTestRule) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -388,9 +388,10 @@ class UserAPI(private val db: SupabaseClient, cachePath: Path) : SupabaseApi() {
}) {
filter {
eq("user_id", userId)
eq("role_id", roleIDToChange.toString().drop(1).dropLast(1))
eq("role_id", roleIDToChange!!["uid"].toString().drop(1).dropLast(1))
}
}

onSuccess()
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,52 +1,52 @@
package com.github.se.assocify.ui.composables

import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.pullrefresh.PullRefreshIndicator
import androidx.compose.material.pullrefresh.pullRefresh
import androidx.compose.material.pullrefresh.rememberPullRefreshState
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp

/**
* A container to make a scrollable content pullable to refresh.
*
* This box is meant to be used as a wrapper around a vertically scrolling element, such as a Column
* or LazyColumn. It will allow the user to pull down on the content to refresh it. The box will
* automatically show a loading indicator when the content is refreshing.
*
* The box will take up the entire size of its parent (typically a Scaffold), and will apply the
* padding values to the content inside the box.
*
* NOTE: Pass the scaffold padding values to the box itself, NOT the content inside the box.
*
* @param refreshing Whether the content is currently refreshing.
* @param onRefresh The callback to call when the user pulls down to refresh.
* @param paddingValues The padding values to apply to the container.
*/
@OptIn(ExperimentalMaterialApi::class)
@Composable
fun PullDownRefreshBox(
refreshing: Boolean,
onRefresh: () -> Unit,
paddingValues: PaddingValues? = null,
modifier: Modifier = Modifier,
content: @Composable () -> Unit = {},
) {
val pullRefreshState = rememberPullRefreshState(refreshing, onRefresh)

Box(
modifier =
modifier
.padding(paddingValues ?: PaddingValues(0.dp))
.fillMaxSize()
.pullRefresh(pullRefreshState)) {
content()
PullRefreshIndicator(refreshing, pullRefreshState, Modifier.align(Alignment.TopCenter))
}
}
package com.github.se.assocify.ui.composables
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.pullrefresh.PullRefreshIndicator
import androidx.compose.material.pullrefresh.pullRefresh
import androidx.compose.material.pullrefresh.rememberPullRefreshState
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
/**
* A container to make a scrollable content pullable to refresh.
*
* This box is meant to be used as a wrapper around a vertically scrolling element, such as a Column
* or LazyColumn. It will allow the user to pull down on the content to refresh it. The box will
* automatically show a loading indicator when the content is refreshing.
*
* The box will take up the entire size of its parent (typically a Scaffold), and will apply the
* padding values to the content inside the box.
*
* NOTE: Pass the scaffold padding values to the box itself, NOT the content inside the box.
*
* @param refreshing Whether the content is currently refreshing.
* @param onRefresh The callback to call when the user pulls down to refresh.
* @param paddingValues The padding values to apply to the container.
*/
@OptIn(ExperimentalMaterialApi::class)
@Composable
fun PullDownRefreshBox(
refreshing: Boolean,
onRefresh: () -> Unit,
paddingValues: PaddingValues? = null,
modifier: Modifier = Modifier,
content: @Composable () -> Unit = {},
) {
val pullRefreshState = rememberPullRefreshState(refreshing, onRefresh)
Box(
modifier =
modifier
.padding(paddingValues ?: PaddingValues(0.dp))
.fillMaxSize()
.pullRefresh(pullRefreshState)) {
content()
PullRefreshIndicator(refreshing, pullRefreshState, Modifier.align(Alignment.TopCenter))
}
}
Loading
Loading