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