diff --git a/front_end/app/src/main/AndroidManifest.xml b/front_end/app/src/main/AndroidManifest.xml index d2ad9267..c35309b6 100644 --- a/front_end/app/src/main/AndroidManifest.xml +++ b/front_end/app/src/main/AndroidManifest.xml @@ -26,6 +26,11 @@ + + \ No newline at end of file diff --git a/front_end/app/src/main/java/com/example/studentportal/course/ui/activtiy/CourseActivity.kt b/front_end/app/src/main/java/com/example/studentportal/course/ui/activtiy/CourseActivity.kt new file mode 100644 index 00000000..9b0c2113 --- /dev/null +++ b/front_end/app/src/main/java/com/example/studentportal/course/ui/activtiy/CourseActivity.kt @@ -0,0 +1,86 @@ +package com.example.studentportal.course.ui.activtiy + +import android.content.Context +import android.content.Intent +import android.os.Bundle +import android.view.MenuItem +import androidx.annotation.VisibleForTesting +import androidx.fragment.app.FragmentActivity +import com.example.studentportal.R +import com.example.studentportal.common.ui.showFragment +import com.example.studentportal.course.ui.fragment.CourseFragment +import com.example.studentportal.course.ui.model.UserType +import com.example.studentportal.databinding.ActivityCourseBinding +import com.example.studentportal.home.ui.model.BaseCourseUiModel + +class CourseActivity : FragmentActivity() { + @VisibleForTesting + internal lateinit var binding: ActivityCourseBinding + + private val userType: UserType + get() { + val name = intent.getStringExtra(KEY_USER_TYPE) + return UserType.valueOf(name.orEmpty()) + } + + private val userId: String + get() { + return intent.getStringExtra(KEY_USER_ID).orEmpty() + } + + private val courseId: String + get() { + return intent.getStringExtra(KEY_COURSE_ID).orEmpty() + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + binding = ActivityCourseBinding.inflate(layoutInflater).initUi() + actionBar?.apply { + setHomeButtonEnabled(true) + setDisplayHomeAsUpEnabled(true) + setHomeAsUpIndicator(R.drawable.arrow_back) + } + supportFragmentManager.showFragment( + fragment = CourseFragment.newInstance( + userType = userType, + userId = userId, + courseId = courseId + ), + addToBackStack = false, + containerId = binding.flContent.id + ) + setContentView(binding.root) + } + + private fun ActivityCourseBinding.initUi(): ActivityCourseBinding { + toolbar.setTitle(intent.getStringExtra(KEY_USER_COURSE_NAME)) + setActionBar(this.toolbar) + return this + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + onBackPressedDispatcher.onBackPressed() + return super.onOptionsItemSelected(item) + } + + companion object { + const val KEY_USER_ID = "KEY_USER_ID" + const val KEY_USER_TYPE = "KEY_USER_TYPE" + const val KEY_COURSE_ID = "KEY_COURSE_ID" + const val KEY_USER_COURSE_NAME = "KEY_COURSE_NAME" + fun intent( + owner: Context, + course: BaseCourseUiModel.CourseUiModel, + userId: String, + userType: String + ): Intent { + val intent = Intent(owner, CourseActivity::class.java) + intent.putExtra(KEY_USER_ID, userId) + intent.putExtra(KEY_USER_TYPE, userType) + intent.putExtra(KEY_COURSE_ID, course.id) + intent.putExtra(KEY_USER_COURSE_NAME, course.name) + return intent + } + } +} diff --git a/front_end/app/src/main/java/com/example/studentportal/course/ui/fragment/CourseFragment.kt b/front_end/app/src/main/java/com/example/studentportal/course/ui/fragment/CourseFragment.kt new file mode 100644 index 00000000..3fbb0d11 --- /dev/null +++ b/front_end/app/src/main/java/com/example/studentportal/course/ui/fragment/CourseFragment.kt @@ -0,0 +1,66 @@ +package com.example.studentportal.course.ui.fragment + +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.core.os.bundleOf +import com.example.studentportal.common.ui.fragment.BaseFragment +import com.example.studentportal.course.ui.layout.CourseMenuLayout +import com.example.studentportal.course.ui.model.Command +import com.example.studentportal.course.ui.model.UserType +import com.example.studentportal.databinding.FragmentCourseBinding + +class CourseFragment : BaseFragment(TAG) { + + private val userType: UserType + get() { + val name = requireArguments().getString(KEY_USER_TYPE) + return UserType.valueOf(name = name.orEmpty()) + } + + override fun inflateBinding( + inflater: LayoutInflater, + container: ViewGroup? + ): FragmentCourseBinding { + val binding = FragmentCourseBinding.inflate(inflater, container, false) + binding.composeView.setContent { + CourseMenuLayout(userType = userType) { + when (it) { + Command.ShowAssignments -> { + // TODO set up assignments + } + Command.ShowContent -> { + // TODO show course content + } + Command.ShowStudents -> { + // TODO show students + } + Command.Nothing -> Unit // Default + } + } + } + return binding + } + + override fun menuItem(): Int = -1 + + companion object { + const val TAG = "COURSE" + const val KEY_USER_TYPE = "KEY_USER_TYPE" + const val KEY_USER_ID = "KEY_USER_ID" + const val KEY_COURSE_ID = "KEY_COURSE_ID" + + fun newInstance( + userType: UserType, + userId: String, + courseId: String + ): CourseFragment { + val fragment = CourseFragment() + fragment.arguments = bundleOf( + KEY_COURSE_ID to courseId, + KEY_USER_ID to userId, + KEY_USER_TYPE to userType.name + ) + return fragment + } + } +} diff --git a/front_end/app/src/main/java/com/example/studentportal/course/ui/layout/CourseMenuLayout.kt b/front_end/app/src/main/java/com/example/studentportal/course/ui/layout/CourseMenuLayout.kt new file mode 100644 index 00000000..939342c1 --- /dev/null +++ b/front_end/app/src/main/java/com/example/studentportal/course/ui/layout/CourseMenuLayout.kt @@ -0,0 +1,103 @@ +package com.example.studentportal.course.ui.layout + +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.wrapContentHeight +import androidx.compose.foundation.layout.wrapContentSize +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.outlined.KeyboardArrowRight +import androidx.compose.material3.Divider +import androidx.compose.material3.Icon +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.focus.FocusRequester.Companion.createRefs +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import androidx.constraintlayout.compose.ConstraintLayout +import com.example.studentportal.course.ui.model.Command +import com.example.studentportal.course.ui.model.CourseMenuItem +import com.example.studentportal.course.ui.model.UserType + +@Composable +fun CourseMenuLayout( + userType: UserType, + modifier: Modifier = Modifier, + onClicked: (command: Command) -> Unit +) { + LazyColumn(modifier) { + items(userType.menuItems) { + MenuItem( + menuItem = it, + onClicked = onClicked + ) + } + } +} + +@Composable +fun MenuItem( + menuItem: CourseMenuItem, + onClicked: (command: Command) -> Unit, + modifier: Modifier = Modifier +) { + ConstraintLayout( + modifier = Modifier + .height(64.dp) + .fillMaxWidth() + .clickable { + onClicked.invoke(menuItem.command) + } + ) { + val (icon, title, arrow, divider) = createRefs() + Icon( + modifier = modifier + .constrainAs(icon) { + top.linkTo(parent.top) + bottom.linkTo(parent.bottom) + start.linkTo(parent.start, margin = 16.dp) + }, + imageVector = menuItem.icon, + contentDescription = null + ) + Text( + modifier = modifier + .wrapContentHeight() + .padding(start = 16.dp, end = 16.dp) + .constrainAs(title) { + top.linkTo(icon.top) + bottom.linkTo(icon.bottom) + start.linkTo(icon.end) + }, + text = stringResource(id = menuItem.titleRes), + textAlign = TextAlign.Start, + style = TextStyle(fontSize = 22.sp) + ) + Icon( + modifier = modifier + .constrainAs(arrow) { + top.linkTo(parent.top) + bottom.linkTo(parent.bottom) + end.linkTo(parent.end, margin = 16.dp) + }, + imageVector = Icons.Outlined.KeyboardArrowRight, + contentDescription = null + ) + Divider( + modifier = modifier + .constrainAs(divider) { + top.linkTo(parent.bottom, margin = 0.dp) + start.linkTo(parent.start, margin = 0.dp) + end.linkTo(parent.end, margin = 0.dp) + } + .wrapContentSize() + ) + } +} diff --git a/front_end/app/src/main/java/com/example/studentportal/course/ui/model/CourseMenuItem.kt b/front_end/app/src/main/java/com/example/studentportal/course/ui/model/CourseMenuItem.kt new file mode 100644 index 00000000..a25eb620 --- /dev/null +++ b/front_end/app/src/main/java/com/example/studentportal/course/ui/model/CourseMenuItem.kt @@ -0,0 +1,17 @@ +package com.example.studentportal.course.ui.model + +import androidx.annotation.StringRes +import androidx.compose.ui.graphics.vector.ImageVector + +data class CourseMenuItem( + @StringRes val titleRes: Int, + val icon: ImageVector, + val command: Command +) + +sealed interface Command { + object ShowStudents : Command + object ShowAssignments : Command + object ShowContent : Command + object Nothing : Command +} diff --git a/front_end/app/src/main/java/com/example/studentportal/course/ui/model/UserType.kt b/front_end/app/src/main/java/com/example/studentportal/course/ui/model/UserType.kt new file mode 100644 index 00000000..13318821 --- /dev/null +++ b/front_end/app/src/main/java/com/example/studentportal/course/ui/model/UserType.kt @@ -0,0 +1,61 @@ +package com.example.studentportal.course.ui.model + +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Create +import androidx.compose.material.icons.filled.Face +import androidx.compose.material.icons.filled.Info +import com.example.studentportal.R + +enum class UserType( + val menuItems: List +) { + FACULTY( + menuItems = listOf( + CourseMenuItem( + titleRes = R.string.courses_menu_content, + icon = Icons.Default.Info, + command = Command.ShowContent + ), + CourseMenuItem( + titleRes = R.string.courses_menu_students, + icon = Icons.Default.Face, + command = Command.ShowStudents + ), + CourseMenuItem( + titleRes = R.string.courses_menu_assignments, + icon = Icons.Default.Create, + command = Command.ShowAssignments + ) + ) + ), + STUDENT( + menuItems = listOf( + CourseMenuItem( + titleRes = R.string.courses_menu_content, + icon = Icons.Default.Info, + command = Command.ShowContent + ), + CourseMenuItem( + titleRes = R.string.courses_menu_assignments, + icon = Icons.Default.Create, + command = Command.ShowAssignments + ) + ) + ), + ADMIN( + menuItems = listOf( + CourseMenuItem( + titleRes = R.string.courses_menu_students, + icon = Icons.Default.Face, + command = Command.ShowStudents + ) + ) + ), + UNKNOWN(listOf()); + + companion object { + fun valueOf(name: String): UserType { + return UserType.entries.firstOrNull { it.name == name } ?: UNKNOWN + } + } +} diff --git a/front_end/app/src/main/java/com/example/studentportal/home/ui/fragment/HomeFragment.kt b/front_end/app/src/main/java/com/example/studentportal/home/ui/fragment/HomeFragment.kt index 2f286ab1..be512b6e 100644 --- a/front_end/app/src/main/java/com/example/studentportal/home/ui/fragment/HomeFragment.kt +++ b/front_end/app/src/main/java/com/example/studentportal/home/ui/fragment/HomeFragment.kt @@ -2,11 +2,16 @@ package com.example.studentportal.home.ui.fragment import android.view.LayoutInflater import android.view.ViewGroup +import androidx.core.os.bundleOf import androidx.fragment.app.viewModels import com.example.studentportal.R import com.example.studentportal.common.ui.fragment.BaseFragment +import com.example.studentportal.course.ui.activtiy.CourseActivity +import com.example.studentportal.course.ui.model.UserType import com.example.studentportal.databinding.FragmentCoursesBinding import com.example.studentportal.home.ui.layout.CoursesLayout +import com.example.studentportal.home.ui.layout.KEY_USER_ID +import com.example.studentportal.home.ui.layout.KEY_USER_TYPE import com.example.studentportal.home.ui.viewmodel.HomeViewModel class HomeFragment : BaseFragment(TAG) { @@ -20,7 +25,20 @@ class HomeFragment : BaseFragment(TAG) { ): FragmentCoursesBinding { val binding = FragmentCoursesBinding.inflate(inflater, container, false) binding.composeView.setContent { - CoursesLayout(viewModel = viewModel) + CoursesLayout( + viewModel = viewModel, + args = requireArguments(), + onClick = { course -> + startActivity( + CourseActivity.intent( + requireActivity(), + course = course, + userId = requireArguments().getString(KEY_USER_ID).orEmpty(), + userType = requireArguments().getString(KEY_USER_TYPE) ?: UserType.UNKNOWN.name + ) + ) + } + ) } return binding } @@ -30,7 +48,12 @@ class HomeFragment : BaseFragment(TAG) { companion object { const val TAG = "HOME" fun newInstance(): HomeFragment { - return HomeFragment() + val fragments = HomeFragment() + fragments.arguments = bundleOf( + KEY_USER_ID to "b0f52f07-86a7-4abe-b71a-9ef9212b303d", + KEY_USER_TYPE to UserType.FACULTY.name + ) + return fragments } } } diff --git a/front_end/app/src/main/java/com/example/studentportal/home/ui/layout/CoursesLayout.kt b/front_end/app/src/main/java/com/example/studentportal/home/ui/layout/CoursesLayout.kt index a78c393a..9686ed9e 100644 --- a/front_end/app/src/main/java/com/example/studentportal/home/ui/layout/CoursesLayout.kt +++ b/front_end/app/src/main/java/com/example/studentportal/home/ui/layout/CoursesLayout.kt @@ -1,6 +1,8 @@ package com.example.studentportal.home.ui.layout +import android.os.Bundle import androidx.compose.foundation.background +import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding @@ -18,7 +20,6 @@ import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.livedata.observeAsState import androidx.compose.ui.Modifier -import androidx.compose.ui.focus.FocusRequester.Companion.createRefs import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.TextStyle @@ -33,12 +34,16 @@ import com.example.studentportal.home.ui.model.BaseCourseUiModel import com.example.studentportal.home.ui.viewmodel.HomeViewModel @Composable -fun CoursesLayout(viewModel: HomeViewModel) { +fun CoursesLayout( + viewModel: HomeViewModel, + args: Bundle, + onClick: (course: BaseCourseUiModel.CourseUiModel) -> Unit +) { val uiState by viewModel.uiResultLiveData.observeAsState() // API call LaunchedEffect(key1 = Unit) { - viewModel.fetchStudent(userId = "b0f52f07-86a7-4abe-b71a-9ef9212b303d") + viewModel.fetchCourses(userId = args.getString(KEY_USER_ID)) } when (uiState) { @@ -48,7 +53,8 @@ fun CoursesLayout(viewModel: HomeViewModel) { if (!courseList.isNullOrEmpty()) { CoursesList( uiModels = courseList, - modifier = Modifier.fillMaxSize() + modifier = Modifier.fillMaxSize(), + onCourseClicked = onClick ) } else { Text(stringResource(id = R.string.courses_empty)) @@ -59,12 +65,17 @@ fun CoursesLayout(viewModel: HomeViewModel) { } @Composable -fun CoursesList(uiModels: List, modifier: Modifier = Modifier) { +fun CoursesList( + uiModels: List, + modifier: Modifier = Modifier, + onCourseClicked: (course: BaseCourseUiModel.CourseUiModel) -> Unit +) { LazyColumn(modifier) { items(uiModels) { item -> when (item) { is BaseCourseUiModel.CourseUiModel -> CourseCard( - course = item + course = item, + onCourseClicked = onCourseClicked ) is BaseCourseUiModel.FacultyUiModel -> FacultyHeaderCard( professor = item @@ -136,8 +147,16 @@ fun SemesterHeaderCard(semester: BaseCourseUiModel.SemesterUiModel, modifier: Mo } @Composable -fun CourseCard(course: BaseCourseUiModel.CourseUiModel, modifier: Modifier = Modifier) { - ConstraintLayout { +fun CourseCard( + course: BaseCourseUiModel.CourseUiModel, + onCourseClicked: (course: BaseCourseUiModel.CourseUiModel) -> Unit, + modifier: Modifier = Modifier +) { + ConstraintLayout( + modifier = Modifier.clickable { + onCourseClicked.invoke(course) + } + ) { val (text1, text2, divider, icon) = createRefs() Text( modifier = modifier @@ -187,3 +206,6 @@ fun CourseCard(course: BaseCourseUiModel.CourseUiModel, modifier: Modifier = Mod ) } } + +val KEY_USER_ID = "KEY_USER_ID" +val KEY_USER_TYPE = "KEY_USER_TYPE" diff --git a/front_end/app/src/main/java/com/example/studentportal/home/ui/viewmodel/HomeViewModel.kt b/front_end/app/src/main/java/com/example/studentportal/home/ui/viewmodel/HomeViewModel.kt index 502f6741..40071350 100644 --- a/front_end/app/src/main/java/com/example/studentportal/home/ui/viewmodel/HomeViewModel.kt +++ b/front_end/app/src/main/java/com/example/studentportal/home/ui/viewmodel/HomeViewModel.kt @@ -30,25 +30,27 @@ class HomeViewModel( val uiResultLiveData: LiveData get() = _uiResultLiveData - suspend fun fetchStudent(userId: String) { - _uiResultLiveData.value = BaseUiState.Loading() - viewModelScope.launch(dispatcher) { - CoursesUseCase(userId = userId, repository = koin.get()) - .launch() - .collectLatest { result -> - when (result) { - is UseCaseResult.Failure -> { - viewModelScope.launch { - _uiResultLiveData.value = result.failure() + suspend fun fetchCourses(userId: String?) { + userId?.let { + _uiResultLiveData.value = BaseUiState.Loading() + viewModelScope.launch(dispatcher) { + CoursesUseCase(userId = userId, repository = koin.get()) + .launch() + .collectLatest { result -> + when (result) { + is UseCaseResult.Failure -> { + viewModelScope.launch { + _uiResultLiveData.value = result.failure() + } } - } - is UseCaseResult.Success -> { - viewModelScope.launch { - _uiResultLiveData.value = result.success() + is UseCaseResult.Success -> { + viewModelScope.launch { + _uiResultLiveData.value = result.success() + } } } } - } + } } } diff --git a/front_end/app/src/main/res/drawable/arrow_back.xml b/front_end/app/src/main/res/drawable/arrow_back.xml new file mode 100644 index 00000000..e8639002 --- /dev/null +++ b/front_end/app/src/main/res/drawable/arrow_back.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/front_end/app/src/main/res/layout/activity_course.xml b/front_end/app/src/main/res/layout/activity_course.xml new file mode 100644 index 00000000..e07550c7 --- /dev/null +++ b/front_end/app/src/main/res/layout/activity_course.xml @@ -0,0 +1,23 @@ + + + + + + + + + \ No newline at end of file diff --git a/front_end/app/src/main/res/layout/fragment_course.xml b/front_end/app/src/main/res/layout/fragment_course.xml new file mode 100644 index 00000000..ec875673 --- /dev/null +++ b/front_end/app/src/main/res/layout/fragment_course.xml @@ -0,0 +1,16 @@ + + + + + + \ No newline at end of file diff --git a/front_end/app/src/main/res/values/strings.xml b/front_end/app/src/main/res/values/strings.xml index 5c3e551b..ee2b4f08 100644 --- a/front_end/app/src/main/res/values/strings.xml +++ b/front_end/app/src/main/res/values/strings.xml @@ -18,4 +18,7 @@ No courses to show + Students + Assignments + Content \ No newline at end of file diff --git a/front_end/app/src/test/java/com/example/studentportal/course/ui/activity/CourseActivityTest.kt b/front_end/app/src/test/java/com/example/studentportal/course/ui/activity/CourseActivityTest.kt new file mode 100644 index 00000000..1d4d3f62 --- /dev/null +++ b/front_end/app/src/test/java/com/example/studentportal/course/ui/activity/CourseActivityTest.kt @@ -0,0 +1,63 @@ +package com.example.studentportal.course.ui.activity + +import android.content.Intent +import androidx.compose.ui.test.assertIsDisplayed +import androidx.compose.ui.test.junit4.createComposeRule +import androidx.compose.ui.test.onNodeWithText +import androidx.compose.ui.test.performClick +import androidx.test.core.app.ActivityScenario +import androidx.test.core.app.ApplicationProvider +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.example.studentportal.course.ui.activtiy.CourseActivity +import com.example.studentportal.course.ui.activtiy.CourseActivity.Companion.KEY_COURSE_ID +import com.example.studentportal.course.ui.fragment.CourseFragment +import com.example.studentportal.course.ui.model.UserType +import com.example.studentportal.home.ui.layout.KEY_USER_ID +import com.example.studentportal.home.ui.layout.KEY_USER_TYPE +import com.google.common.truth.Truth.assertThat +import org.junit.After +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.koin.core.context.stopKoin + +@RunWith(AndroidJUnit4::class) +class CourseActivityTest { + + @get:Rule + val composeTestRule = createComposeRule() + + @After + fun tearDown() { + stopKoin() + } + + @Test + fun `test initial setup`() { + val intent = Intent(ApplicationProvider.getApplicationContext(), CourseActivity::class.java) + intent.putExtra(KEY_USER_TYPE, UserType.FACULTY.name) + intent.putExtra(KEY_USER_ID, "userId") + intent.putExtra(KEY_COURSE_ID, "courseId") + ActivityScenario.launch(intent) + .use { scenario -> + scenario.onActivity { activity -> + val fragmentManager = activity.supportFragmentManager + + // Verify homeFragment is displayed + val fragment = fragmentManager.findFragmentByTag(CourseFragment.TAG) + assertThat(fragment).isNotNull() + + // Verify options + composeTestRule.onNodeWithText("Content").assertIsDisplayed() + composeTestRule.onNodeWithText("Content").performClick() // Consume click + composeTestRule.onNodeWithText("Students").assertIsDisplayed() + composeTestRule.onNodeWithText("Students").performClick() // Consume click + composeTestRule.onNodeWithText("Assignments").assertIsDisplayed() + composeTestRule.onNodeWithText("Assignments").performClick() // Consume click + + // Verify backstack is empty + assertThat(fragmentManager.backStackEntryCount).isEqualTo(0) + } + } + } +} diff --git a/front_end/app/src/test/java/com/example/studentportal/course/ui/fragment/CourseFragmentTest.kt b/front_end/app/src/test/java/com/example/studentportal/course/ui/fragment/CourseFragmentTest.kt new file mode 100644 index 00000000..674d45d0 --- /dev/null +++ b/front_end/app/src/test/java/com/example/studentportal/course/ui/fragment/CourseFragmentTest.kt @@ -0,0 +1,3 @@ +package com.example.studentportal.course.ui.fragment + +class CourseFragmentTest diff --git a/front_end/app/src/test/java/com/example/studentportal/home/ui/fragment/HomeFragmentTest.kt b/front_end/app/src/test/java/com/example/studentportal/home/ui/fragment/HomeFragmentTest.kt index 3484f955..6edd25f5 100644 --- a/front_end/app/src/test/java/com/example/studentportal/home/ui/fragment/HomeFragmentTest.kt +++ b/front_end/app/src/test/java/com/example/studentportal/home/ui/fragment/HomeFragmentTest.kt @@ -3,11 +3,15 @@ package com.example.studentportal.home.ui.fragment import androidx.compose.ui.test.assertIsDisplayed import androidx.compose.ui.test.junit4.createComposeRule import androidx.compose.ui.test.onNodeWithText +import androidx.core.os.bundleOf import androidx.fragment.app.testing.launchFragmentInContainer import androidx.lifecycle.Lifecycle import androidx.test.ext.junit.runners.AndroidJUnit4 import com.example.studentportal.common.ui.model.BaseUiState import com.example.studentportal.common.usecase.DefaultError +import com.example.studentportal.course.ui.model.UserType +import com.example.studentportal.home.ui.layout.KEY_USER_ID +import com.example.studentportal.home.ui.layout.KEY_USER_TYPE import com.example.studentportal.home.ui.model.BaseCourseUiModel import com.example.studentportal.home.ui.model.CourseListUiModel import org.junit.After @@ -30,7 +34,12 @@ class HomeFragmentTest { @Test fun `test fetch courses loading`() { - launchFragmentInContainer().onFragment { + launchFragmentInContainer( + bundleOf( + KEY_USER_ID to "userId", + KEY_USER_TYPE to UserType.FACULTY.name + ) + ).onFragment { composeTestRule.onNodeWithText("Loading...").assertIsDisplayed() } } @@ -38,7 +47,12 @@ class HomeFragmentTest { @Test fun `test fetch course success`() { val date = Date() - launchFragmentInContainer().onFragment { homeFragment -> + launchFragmentInContainer( + bundleOf( + KEY_USER_ID to "userId", + KEY_USER_TYPE to UserType.FACULTY.name + ) + ).onFragment { homeFragment -> homeFragment.viewModel._uiResultLiveData.value = BaseUiState.Success( CourseListUiModel( uiModels = listOf( @@ -84,7 +98,12 @@ class HomeFragmentTest { @Test fun `test fetch courses error`() { - launchFragmentInContainer().onFragment { homeFragment -> + launchFragmentInContainer( + bundleOf( + KEY_USER_ID to "userId", + KEY_USER_TYPE to UserType.FACULTY.name + ) + ).onFragment { homeFragment -> homeFragment.viewModel._uiResultLiveData.value = BaseUiState.Error( DefaultError("Error Loading User") ) @@ -95,7 +114,12 @@ class HomeFragmentTest { @Test(expected = IllegalAccessException::class) fun `expect exception when binding is accessed after UI is destroyed`() { var fragment: HomeFragment? = null - launchFragmentInContainer().onFragment { + launchFragmentInContainer( + bundleOf( + KEY_USER_ID to "userId", + KEY_USER_TYPE to UserType.FACULTY.name + ) + ).onFragment { fragment = it }.moveToState(Lifecycle.State.DESTROYED) fragment?.binding // Force Crash diff --git a/front_end/app/src/test/java/com/example/studentportal/home/ui/viewmodel/HomeViewModelTest.kt b/front_end/app/src/test/java/com/example/studentportal/home/ui/viewmodel/HomeViewModelTest.kt index 1a436cd2..4a7484bc 100644 --- a/front_end/app/src/test/java/com/example/studentportal/home/ui/viewmodel/HomeViewModelTest.kt +++ b/front_end/app/src/test/java/com/example/studentportal/home/ui/viewmodel/HomeViewModelTest.kt @@ -77,7 +77,7 @@ class HomeViewModelTest { ) // Act - viewModel.fetchStudent("Id") + viewModel.fetchCourses("Id") // Verify Success Result assertThat(viewModel.uiResultLiveData.value?.isLoading()).isTrue() @@ -114,7 +114,7 @@ class HomeViewModelTest { ) // Act - viewModel.fetchStudent("Id") + viewModel.fetchCourses("Id") mainDispatcher.scheduler.advanceUntilIdle() // Verify Success Result @@ -151,7 +151,7 @@ class HomeViewModelTest { ) // Act - viewModel.fetchStudent("Id") + viewModel.fetchCourses("Id") mainDispatcher.scheduler.advanceUntilIdle() // Verify Success Result