diff --git a/donext.backup b/donext.backup new file mode 100644 index 0000000..c33c48f Binary files /dev/null and b/donext.backup differ diff --git a/donextv2/build.gradle.kts b/donextv2/build.gradle.kts index 8da0d34..0aeda86 100644 --- a/donextv2/build.gradle.kts +++ b/donextv2/build.gradle.kts @@ -80,6 +80,9 @@ dependencies { implementation("androidx.navigation:navigation-compose:2.9.5") implementation("androidx.hilt:hilt-navigation-compose:1.3.0") implementation("sh.calvin.reorderable:reorderable:3.0.0") + testImplementation("junit:junit:4.13.2") + testImplementation("io.mockk:mockk:1.13.12") + testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.9.0") androidTestImplementation("androidx.test.ext:junit-ktx:1.3.0") androidTestImplementation(platform("androidx.compose:compose-bom:2025.10.01")) androidTestImplementation("androidx.compose.ui:ui-test-junit4") diff --git a/donextv2/src/test/java/com/wismna/geoffroy/donext/presentation/viewmodel/DueTodayViewModelTest.kt b/donextv2/src/test/java/com/wismna/geoffroy/donext/presentation/viewmodel/DueTodayViewModelTest.kt new file mode 100644 index 0000000..1428e93 --- /dev/null +++ b/donextv2/src/test/java/com/wismna/geoffroy/donext/presentation/viewmodel/DueTodayViewModelTest.kt @@ -0,0 +1,111 @@ +package com.wismna.geoffroy.donext.presentation.viewmodel + +import com.wismna.geoffroy.donext.R +import com.wismna.geoffroy.donext.domain.model.Priority +import com.wismna.geoffroy.donext.domain.model.Task +import com.wismna.geoffroy.donext.domain.usecase.GetDueTodayTasksUseCase +import com.wismna.geoffroy.donext.domain.usecase.ToggleTaskDeletedUseCase +import com.wismna.geoffroy.donext.domain.usecase.ToggleTaskDoneUseCase +import com.wismna.geoffroy.donext.presentation.ui.events.UiEvent +import com.wismna.geoffroy.donext.presentation.ui.events.UiEventBus +import io.mockk.* +import junit.framework.TestCase.assertEquals +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.test.* +import org.junit.After +import org.junit.Before +import org.junit.Test + +@OptIn(ExperimentalCoroutinesApi::class) +class DueTodayViewModelTest { + + private lateinit var getDueTodayTasksUseCase: GetDueTodayTasksUseCase + private lateinit var toggleTaskDeletedUseCase: ToggleTaskDeletedUseCase + private lateinit var toggleTaskDoneUseCase: ToggleTaskDoneUseCase + private lateinit var uiEventBus: UiEventBus + private lateinit var viewModel: DueTodayViewModel + + private val testDispatcher = StandardTestDispatcher() + private val tasksFlow = MutableSharedFlow>(replay = 1) + + @Before + fun setup() { + Dispatchers.setMain(testDispatcher) + + getDueTodayTasksUseCase = mockk() + toggleTaskDeletedUseCase = mockk(relaxed = true) + toggleTaskDoneUseCase = mockk(relaxed = true) + uiEventBus = mockk(relaxed = true) + + coEvery { getDueTodayTasksUseCase.invoke() } returns tasksFlow + + viewModel = DueTodayViewModel( + getDueTodayTasksUseCase, + toggleTaskDeletedUseCase, + toggleTaskDoneUseCase, + uiEventBus + ) + } + + @After + fun tearDown() { + Dispatchers.resetMain() + } + + @Test + fun `dueTodayTasks updates when flow emits`() = runTest { + val taskList = listOf(Task(taskListId = 0, id = 1, name = "Test Task", description = "", priority = Priority.NORMAL, isDone = false, isDeleted = false)) + tasksFlow.emit(taskList) + advanceUntilIdle() + + assertEquals(taskList, viewModel.dueTodayTasks) + } + + @Test + fun `onTaskClicked sends EditTask event`() = runTest { + val task = Task(taskListId = 0, id = 42, name = "Click me", description = "", priority = Priority.NORMAL, isDone = false, isDeleted = false) + + viewModel.onTaskClicked(task) + advanceUntilIdle() + + coVerify { uiEventBus.send(UiEvent.EditTask(task)) } + } + + @Test + fun `updateTaskDone toggles done and sends snackbar with undo`() = runTest { + val taskId = 5L + + viewModel.updateTaskDone(taskId) + advanceUntilIdle() + + coVerify { toggleTaskDoneUseCase(taskId, true) } + coVerify { + uiEventBus.send( + match { + it is UiEvent.ShowUndoSnackbar && + it.message == R.string.snackbar_message_task_done + } + ) + } + } + + @Test + fun `deleteTask toggles deleted and sends snackbar with undo`() = runTest { + val taskId = 7L + + viewModel.deleteTask(taskId) + advanceUntilIdle() + + coVerify { toggleTaskDeletedUseCase(taskId, true) } + coVerify { + uiEventBus.send( + match{ + it is UiEvent.ShowUndoSnackbar && + it.message == R.string.snackbar_message_task_recycle + } + ) + } + } +}