diff --git a/.editorconfig b/.editorconfig index a484e453..dfdd731a 100644 --- a/.editorconfig +++ b/.editorconfig @@ -2,3 +2,6 @@ root = true [*.{kt,kts}] ktlint_standard_blank-line-before-declaration=disabled +ktlint_standard_multiline-expression-wrapping = disabled +ktlint_standard_string-template-indent = disabled +ktlint_function_signature_body_expression_wrapping = default diff --git a/domain/src/main/java/br/com/sailboat/todozy/domain/model/TaskProgressRange.kt b/domain/src/main/java/br/com/sailboat/todozy/domain/model/TaskProgressRange.kt index 54a17631..2a401436 100644 --- a/domain/src/main/java/br/com/sailboat/todozy/domain/model/TaskProgressRange.kt +++ b/domain/src/main/java/br/com/sailboat/todozy/domain/model/TaskProgressRange.kt @@ -9,11 +9,10 @@ enum class TaskProgressRange { LAST_7_DAYS, ; - fun startDate(today: LocalDate): LocalDate = - when (this) { - ALL -> today - LAST_YEAR -> today.minusMonths(12).plusDays(1) - LAST_30_DAYS -> today.minusDays(29) - LAST_7_DAYS -> today.minusDays(6) - } + fun startDate(today: LocalDate): LocalDate = when (this) { + ALL -> today + LAST_YEAR -> today.minusMonths(12) + LAST_30_DAYS -> today.minusDays(29) + LAST_7_DAYS -> today.minusDays(6) + } } diff --git a/feature/about/impl/src/main/java/br/com/sailboat/todozy/feature/about/impl/presentation/AboutFragment.kt b/feature/about/impl/src/main/java/br/com/sailboat/todozy/feature/about/impl/presentation/AboutFragment.kt index cbec2378..062ee144 100644 --- a/feature/about/impl/src/main/java/br/com/sailboat/todozy/feature/about/impl/presentation/AboutFragment.kt +++ b/feature/about/impl/src/main/java/br/com/sailboat/todozy/feature/about/impl/presentation/AboutFragment.kt @@ -64,23 +64,20 @@ internal class AboutFragment : Fragment() { initRecyclerView() } - private fun initToolbar() = - with(binding) { - val appCompatActivity = activity as AppCompatActivity - appCompatActivity.setSupportActionBar(toolbar) - appCompatActivity.supportActionBar?.setDisplayHomeAsUpEnabled(true) - toolbar.setNavigationOnClickListener { requireActivity().onBackPressedDispatcher.onBackPressed() } - toolbar.setTitle(UiR.string.about) - } + private fun initToolbar() = with(binding) { + val appCompatActivity = activity as AppCompatActivity + appCompatActivity.setSupportActionBar(toolbar) + appCompatActivity.supportActionBar?.setDisplayHomeAsUpEnabled(true) + toolbar.setNavigationOnClickListener { requireActivity().onBackPressedDispatcher.onBackPressed() } + toolbar.setTitle(UiR.string.about) + } - private fun initRecyclerView() = - with(binding) { - recycler.layoutManager = LinearLayoutManager(activity) - recycler.adapter = - AboutAdapter().apply { - aboutAdapter = this - } + private fun initRecyclerView() = with(binding) { + recycler.layoutManager = LinearLayoutManager(activity) + recycler.adapter = AboutAdapter().apply { + aboutAdapter = this } + } private fun showErrorLoadingAbout() { Toast.makeText(activity, UiR.string.msg_error, Toast.LENGTH_SHORT).show() diff --git a/feature/about/impl/src/main/java/br/com/sailboat/todozy/feature/about/impl/presentation/GetAboutView.kt b/feature/about/impl/src/main/java/br/com/sailboat/todozy/feature/about/impl/presentation/GetAboutView.kt index 438c6bde..ce89459b 100644 --- a/feature/about/impl/src/main/java/br/com/sailboat/todozy/feature/about/impl/presentation/GetAboutView.kt +++ b/feature/about/impl/src/main/java/br/com/sailboat/todozy/feature/about/impl/presentation/GetAboutView.kt @@ -7,16 +7,15 @@ import br.com.sailboat.uicomponent.model.UiModel import br.com.sailboat.uicomponent.impl.R as UiR internal class GetAboutView(private val context: Context) : GetAboutViewUseCase { - override suspend fun invoke(): Result> = - runCatching { - val items = ArrayList() - items.add(getImageWithTitle()) - items.add(getAppDescription()) - items.add(getVersion()) - items.add(getDevelopedBy()) + override suspend fun invoke(): Result> = runCatching { + val items = ArrayList() + items.add(getImageWithTitle()) + items.add(getAppDescription()) + items.add(getVersion()) + items.add(getDevelopedBy()) - return@runCatching items - } + return@runCatching items + } private fun getImageWithTitle(): ImageTitleDividerUiModel { return ImageTitleDividerUiModel( diff --git a/feature/about/impl/src/main/java/br/com/sailboat/todozy/feature/about/impl/presentation/viewmodel/AboutViewModel.kt b/feature/about/impl/src/main/java/br/com/sailboat/todozy/feature/about/impl/presentation/viewmodel/AboutViewModel.kt index 031ae822..e39c236b 100644 --- a/feature/about/impl/src/main/java/br/com/sailboat/todozy/feature/about/impl/presentation/viewmodel/AboutViewModel.kt +++ b/feature/about/impl/src/main/java/br/com/sailboat/todozy/feature/about/impl/presentation/viewmodel/AboutViewModel.kt @@ -17,13 +17,12 @@ internal class AboutViewModel( } } - private fun onStart() = - viewModelScope.launch { - try { - viewState.itemViews.value = getAboutViewUseCase().getOrThrow() - } catch (e: Exception) { - logService.error(e) - viewState.viewAction.value = AboutViewAction.ShowErrorLoadingAbout - } + private fun onStart() = viewModelScope.launch { + try { + viewState.itemViews.value = getAboutViewUseCase().getOrThrow() + } catch (e: Exception) { + logService.error(e) + viewState.viewAction.value = AboutViewAction.ShowErrorLoadingAbout } + } } diff --git a/feature/alarm/impl/src/main/java/br/com/sailboat/todozy/feature/alarm/impl/data/datasource/AlarmLocalDataSource.kt b/feature/alarm/impl/src/main/java/br/com/sailboat/todozy/feature/alarm/impl/data/datasource/AlarmLocalDataSource.kt index e44c3e0e..8edd2a75 100644 --- a/feature/alarm/impl/src/main/java/br/com/sailboat/todozy/feature/alarm/impl/data/datasource/AlarmLocalDataSource.kt +++ b/feature/alarm/impl/src/main/java/br/com/sailboat/todozy/feature/alarm/impl/data/datasource/AlarmLocalDataSource.kt @@ -4,6 +4,7 @@ import br.com.sailboat.todozy.feature.alarm.impl.data.model.AlarmData internal interface AlarmLocalDataSource { fun getAlarmByTask(taskId: Long): Result + fun getAlarmsByTaskIds(taskIds: List): Result> fun deleteByTask(taskId: Long): Result fun update(alarmData: AlarmData): Result fun save(alarmData: AlarmData): Result diff --git a/feature/alarm/impl/src/main/java/br/com/sailboat/todozy/feature/alarm/impl/data/datasource/AlarmLocalDataSourceSQLite.kt b/feature/alarm/impl/src/main/java/br/com/sailboat/todozy/feature/alarm/impl/data/datasource/AlarmLocalDataSourceSQLite.kt index fce6f13f..6be45142 100644 --- a/feature/alarm/impl/src/main/java/br/com/sailboat/todozy/feature/alarm/impl/data/datasource/AlarmLocalDataSourceSQLite.kt +++ b/feature/alarm/impl/src/main/java/br/com/sailboat/todozy/feature/alarm/impl/data/datasource/AlarmLocalDataSourceSQLite.kt @@ -12,84 +12,100 @@ import java.util.Calendar internal class AlarmLocalDataSourceSQLite( database: DatabaseOpenHelperService, ) : BaseSQLite(database), AlarmLocalDataSource { - override fun getAlarmByTask(taskId: Long): Result = - runCatching { - val sb = StringBuilder() - sb.append(" SELECT Alarm.* FROM Alarm ") - sb.append(" WHERE Alarm.fkTaskId = $taskId") + override fun getAlarmByTask(taskId: Long): Result = runCatching { + val sb = StringBuilder() + sb.append(" SELECT Alarm.* FROM Alarm ") + sb.append(" WHERE Alarm.fkTaskId = $taskId") - val cursor = performQuery(sb.toString()) + val cursor = performQuery(sb.toString()) - if (cursor.moveToNext()) { - val alarm = buildFromCursor(cursor) - cursor.close() - return@runCatching alarm - } - - return@runCatching null - } - - override fun deleteByTask(taskId: Long): Result = - runCatching { - val sql = " DELETE FROM Alarm WHERE Alarm.fkTaskId = ?" - val statement = compileStatement(sql) - statement.bindLong(1, taskId) - - delete(statement) + if (cursor.moveToNext()) { + val alarm = buildFromCursor(cursor) + cursor.close() + return@runCatching alarm } - fun deleteAlarmById(alarmId: Long): Result = - runCatching { - val sql = " DELETE FROM Alarm WHERE Alarm.id = ?" - val statement = compileStatement(sql) - statement.bindLong(1, alarmId) + return@runCatching null + } - delete(statement) + override fun getAlarmsByTaskIds(taskIds: List): Result> = runCatching { + if (taskIds.isEmpty()) { + return@runCatching emptyList() } - override fun save(alarmData: AlarmData): Result = - runCatching { - val sb = StringBuilder() + val sb = StringBuilder() + sb.append(" SELECT Alarm.* FROM Alarm ") + sb.append(" WHERE Alarm.fkTaskId IN (") + sb.append(taskIds.joinToString(",")) + sb.append(")") - sb.append(" INSERT INTO Alarm ") - sb.append(" (fkTaskId, repeatType, nextAlarmDate, insertingDate, days) ") - sb.append(" VALUES (?, ?, ?, ?, ?); ") - - val statement = compileStatement(sb.toString()) - statement.bindLong(1, alarmData.taskId) - statement.bindLong(2, alarmData.repeatType.toLong()) - statement.bindString(3, alarmData.nextAlarmDate.orEmpty()) - statement.bindString(4, Calendar.getInstance().toDateTimeString()) - statement.bindString(5, alarmData.days.orEmpty()) - - insert(statement) - } + val cursor = performQuery(sb.toString()) + val alarms = mutableListOf() - override fun update(alarmData: AlarmData): Result = - runCatching { - val sql = StringBuilder() - sql.append(" UPDATE Alarm SET ") - sql.append(" repeatType = ?, ") - sql.append(" nextAlarmDate = ?, ") - sql.append(" days = ? ") - sql.append(" WHERE id = ? ") - - val statement = compileStatement(sql.toString()) - statement.bindLong(1, alarmData.repeatType.toLong()) - statement.bindString(2, alarmData.nextAlarmDate ?: "") - statement.bindString(3, alarmData.days ?: "") - statement.bindLong(4, alarmData.id) - - update(statement) + while (cursor.moveToNext()) { + alarms.add(buildFromCursor(cursor)) } - private fun buildFromCursor(cursor: Cursor) = - AlarmData( - id = getLong(cursor, "id") ?: Entity.NO_ID, - taskId = getLong(cursor, "fkTaskId") ?: Entity.NO_ID, - repeatType = getInt(cursor, "repeatType") ?: RepeatType.NOT_REPEAT.ordinal, - nextAlarmDate = getString(cursor, "nextAlarmDate"), - insertingDate = getString(cursor, "insertingDate"), - days = getString(cursor, "days"), - ) + cursor.close() + alarms + } + + override fun deleteByTask(taskId: Long): Result = runCatching { + val sql = " DELETE FROM Alarm WHERE Alarm.fkTaskId = ?" + val statement = compileStatement(sql) + statement.bindLong(1, taskId) + + delete(statement) + } + + fun deleteAlarmById(alarmId: Long): Result = runCatching { + val sql = " DELETE FROM Alarm WHERE Alarm.id = ?" + val statement = compileStatement(sql) + statement.bindLong(1, alarmId) + + delete(statement) + } + + override fun save(alarmData: AlarmData): Result = runCatching { + val sb = StringBuilder() + + sb.append(" INSERT INTO Alarm ") + sb.append(" (fkTaskId, repeatType, nextAlarmDate, insertingDate, days) ") + sb.append(" VALUES (?, ?, ?, ?, ?); ") + + val statement = compileStatement(sb.toString()) + statement.bindLong(1, alarmData.taskId) + statement.bindLong(2, alarmData.repeatType.toLong()) + statement.bindString(3, alarmData.nextAlarmDate.orEmpty()) + statement.bindString(4, Calendar.getInstance().toDateTimeString()) + statement.bindString(5, alarmData.days.orEmpty()) + + insert(statement) + } + + override fun update(alarmData: AlarmData): Result = runCatching { + val sql = StringBuilder() + sql.append(" UPDATE Alarm SET ") + sql.append(" repeatType = ?, ") + sql.append(" nextAlarmDate = ?, ") + sql.append(" days = ? ") + sql.append(" WHERE id = ? ") + + val statement = compileStatement(sql.toString()) + statement.bindLong(1, alarmData.repeatType.toLong()) + statement.bindString(2, alarmData.nextAlarmDate ?: "") + statement.bindString(3, alarmData.days ?: "") + statement.bindLong(4, alarmData.id) + + update(statement) + } + + private fun buildFromCursor(cursor: Cursor) = AlarmData( + id = getLong(cursor, "id") ?: Entity.NO_ID, + taskId = getLong(cursor, "fkTaskId") ?: Entity.NO_ID, + repeatType = getInt(cursor, "repeatType") ?: RepeatType.NOT_REPEAT.ordinal, + nextAlarmDate = getString(cursor, "nextAlarmDate"), + insertingDate = getString(cursor, "insertingDate"), + days = getString(cursor, "days"), + ) } diff --git a/feature/alarm/impl/src/main/java/br/com/sailboat/todozy/feature/alarm/impl/data/repository/AlarmRepositoryImpl.kt b/feature/alarm/impl/src/main/java/br/com/sailboat/todozy/feature/alarm/impl/data/repository/AlarmRepositoryImpl.kt index 3d88c065..e4f6c9da 100644 --- a/feature/alarm/impl/src/main/java/br/com/sailboat/todozy/feature/alarm/impl/data/repository/AlarmRepositoryImpl.kt +++ b/feature/alarm/impl/src/main/java/br/com/sailboat/todozy/feature/alarm/impl/data/repository/AlarmRepositoryImpl.kt @@ -11,12 +11,25 @@ internal class AlarmRepositoryImpl( private val alarmDataToAlarmMapper: AlarmDataToAlarmMapper, private val alarmToAlarmDataMapper: AlarmToAlarmDataMapper, ) : AlarmRepository { - override suspend fun getAlarmByTaskId(taskId: Long): Result = - runCatching { - val alarmData = alarmLocalDataSource.getAlarmByTask(taskId).getOrNull() - return@runCatching alarmData?.run { alarmDataToAlarmMapper.map(this) } + override suspend fun getAlarmByTaskId(taskId: Long): Result = runCatching { + val alarmData = alarmLocalDataSource.getAlarmByTask(taskId).getOrNull() + return@runCatching alarmData?.run { alarmDataToAlarmMapper.map(this) } + } + + override suspend fun getAlarmsByTaskIds(taskIds: List): Result> = runCatching { + if (taskIds.isEmpty()) { + return@runCatching emptyMap() } + val alarms = alarmLocalDataSource.getAlarmsByTaskIds(taskIds).getOrThrow() + .mapNotNull { alarmData -> + val alarm = alarmDataToAlarmMapper.map(alarmData) ?: return@mapNotNull null + alarmData.taskId to alarm + } + + return@runCatching alarms.toMap() + } + override suspend fun deleteAlarmByTask(taskId: Long): Result { return alarmLocalDataSource.deleteByTask(taskId) } @@ -24,10 +37,9 @@ internal class AlarmRepositoryImpl( override suspend fun save( alarm: Alarm, taskId: Long, - ): Result = - runCatching { - val alarmData = alarmToAlarmDataMapper.map(alarm, taskId) - alarmLocalDataSource.save(alarmData).getOrThrow() - return@runCatching - } + ): Result = runCatching { + val alarmData = alarmToAlarmDataMapper.map(alarm, taskId) + alarmLocalDataSource.save(alarmData).getOrThrow() + return@runCatching + } } diff --git a/feature/alarm/impl/src/main/java/br/com/sailboat/todozy/feature/alarm/impl/domain/usecase/DeleteAlarmUseCaseImpl.kt b/feature/alarm/impl/src/main/java/br/com/sailboat/todozy/feature/alarm/impl/domain/usecase/DeleteAlarmUseCaseImpl.kt index d9259a77..2831b34e 100644 --- a/feature/alarm/impl/src/main/java/br/com/sailboat/todozy/feature/alarm/impl/domain/usecase/DeleteAlarmUseCaseImpl.kt +++ b/feature/alarm/impl/src/main/java/br/com/sailboat/todozy/feature/alarm/impl/domain/usecase/DeleteAlarmUseCaseImpl.kt @@ -7,9 +7,8 @@ internal class DeleteAlarmUseCaseImpl( private val alarmRepository: AlarmRepository, private val cancelAlarmScheduleUseCase: CancelAlarmScheduleUseCase, ) : DeleteAlarmUseCase { - override suspend operator fun invoke(taskId: Long): Result = - runCatching { - alarmRepository.deleteAlarmByTask(taskId).getOrThrow() - cancelAlarmScheduleUseCase(taskId) - } + override suspend operator fun invoke(taskId: Long): Result = runCatching { + alarmRepository.deleteAlarmByTask(taskId).getOrThrow() + cancelAlarmScheduleUseCase(taskId) + } } diff --git a/feature/alarm/impl/src/main/java/br/com/sailboat/todozy/feature/alarm/impl/domain/usecase/SaveAlarmUseCaseImpl.kt b/feature/alarm/impl/src/main/java/br/com/sailboat/todozy/feature/alarm/impl/domain/usecase/SaveAlarmUseCaseImpl.kt index 7389e322..94cd7c3a 100644 --- a/feature/alarm/impl/src/main/java/br/com/sailboat/todozy/feature/alarm/impl/domain/usecase/SaveAlarmUseCaseImpl.kt +++ b/feature/alarm/impl/src/main/java/br/com/sailboat/todozy/feature/alarm/impl/domain/usecase/SaveAlarmUseCaseImpl.kt @@ -12,10 +12,9 @@ internal class SaveAlarmUseCaseImpl( override suspend operator fun invoke( alarm: Alarm, taskId: Long, - ): Result = - runCatching { - alarmRepository.save(alarm, taskId).getOrThrow() - cancelAlarmScheduleUseCase(taskId) - scheduleAlarmUseCase(alarm, taskId) - } + ): Result = runCatching { + alarmRepository.save(alarm, taskId).getOrThrow() + cancelAlarmScheduleUseCase(taskId) + scheduleAlarmUseCase(alarm, taskId) + } } diff --git a/feature/alarm/impl/src/test/java/br/com/sailboat/todozy/feature/alarm/impl/data/repository/AlarmRepositoryImplTest.kt b/feature/alarm/impl/src/test/java/br/com/sailboat/todozy/feature/alarm/impl/data/repository/AlarmRepositoryImplTest.kt index 05f58273..90d45b7a 100644 --- a/feature/alarm/impl/src/test/java/br/com/sailboat/todozy/feature/alarm/impl/data/repository/AlarmRepositoryImplTest.kt +++ b/feature/alarm/impl/src/test/java/br/com/sailboat/todozy/feature/alarm/impl/data/repository/AlarmRepositoryImplTest.kt @@ -30,113 +30,127 @@ internal class AlarmRepositoryImplTest { ) @Test - fun `should call getAlarmByTask from data source when getAlarmByTaskId is called from repository`() = - runBlocking { - val taskId = 45L - val alarmData = - makeAlarmData( - taskId = taskId, - repeatType = RepeatType.WEEK.ordinal, - nextAlarmDate = "2022-02-23 08:40:00", - ) - val alarm = - Alarm( - dateTime = - with(Calendar.getInstance()) { - set(Calendar.YEAR, 2022) - set(Calendar.MONTH, 1) - set(Calendar.HOUR_OF_DAY, 8) - set(Calendar.DAY_OF_MONTH, 23) - set(Calendar.MINUTE, 40) - set(Calendar.SECOND, 0) - set(Calendar.MILLISECOND, 0) - this - }, - repeatType = RepeatType.WEEK, - ) - val alarmResult = Result.success(alarm) - prepareScenario( - alarmData = alarmData, - alarm = alarm, + fun `should call getAlarmByTask from data source when getAlarmByTaskId is called from repository`() = runBlocking { + val taskId = 45L + val alarmData = + makeAlarmData( + taskId = taskId, + repeatType = RepeatType.WEEK.ordinal, + nextAlarmDate = "2022-02-23 08:40:00", ) + val alarm = + Alarm( + dateTime = + with(Calendar.getInstance()) { + set(Calendar.YEAR, 2022) + set(Calendar.MONTH, 1) + set(Calendar.HOUR_OF_DAY, 8) + set(Calendar.DAY_OF_MONTH, 23) + set(Calendar.MINUTE, 40) + set(Calendar.SECOND, 0) + set(Calendar.MILLISECOND, 0) + this + }, + repeatType = RepeatType.WEEK, + ) + val alarmResult = Result.success(alarm) + prepareScenario( + alarmData = alarmData, + alarm = alarm, + ) + + val result = alarmRepository.getAlarmByTaskId(taskId) + + assertEquals(alarmResult, result) + coVerify { alarmLocalDataSource.getAlarmByTask(taskId) } + } + + @Test + fun `should call deleteByTask from data source when deleteAlarmByTask is called from repository`() = runBlocking { + val taskId = 45L + prepareScenario() - val result = alarmRepository.getAlarmByTaskId(taskId) + alarmRepository.deleteAlarmByTask(taskId) - assertEquals(alarmResult, result) - coVerify { alarmLocalDataSource.getAlarmByTask(taskId) } - } + coVerify { alarmLocalDataSource.deleteByTask(taskId) } + } @Test - fun `should call deleteByTask from data source when deleteAlarmByTask is called from repository`() = - runBlocking { - val taskId = 45L - prepareScenario() + fun `should return null alarm when mapping fails`() = runBlocking { + val taskId = 99L + val alarmData = + makeAlarmData( + taskId = taskId, + nextAlarmDate = "invalid-date", + ) + coEvery { alarmLocalDataSource.getAlarmByTask(taskId) } returns Result.success(alarmData) + coEvery { alarmDataToAlarmMapper.map(alarmData) } returns null - alarmRepository.deleteAlarmByTask(taskId) + val result = alarmRepository.getAlarmByTaskId(taskId).getOrThrow() - coVerify { alarmLocalDataSource.deleteByTask(taskId) } - } + assertNull(result) + coVerify { alarmLocalDataSource.getAlarmByTask(taskId) } + } @Test - fun `should return null alarm when mapping fails`() = - runBlocking { - val taskId = 99L - val alarmData = - makeAlarmData( - taskId = taskId, - nextAlarmDate = "invalid-date", - ) - coEvery { alarmLocalDataSource.getAlarmByTask(taskId) } returns Result.success(alarmData) - coEvery { alarmDataToAlarmMapper.map(alarmData) } returns null - - val result = alarmRepository.getAlarmByTaskId(taskId).getOrThrow() - - assertNull(result) - coVerify { alarmLocalDataSource.getAlarmByTask(taskId) } - } + fun `should call save from data source when save is called from repository`() = runBlocking { + val taskId = 45L + val alarmData = + makeAlarmData( + id = -1, + taskId = taskId, + repeatType = RepeatType.WEEK.ordinal, + nextAlarmDate = "2022-02-23 08:40:00", + insertingDate = null, + ) + val alarm = + makeAlarm( + dateTime = + with(Calendar.getInstance()) { + set(Calendar.YEAR, 2022) + set(Calendar.MONTH, 1) + set(Calendar.HOUR_OF_DAY, 8) + set(Calendar.DAY_OF_MONTH, 23) + set(Calendar.MINUTE, 40) + set(Calendar.SECOND, 0) + set(Calendar.MILLISECOND, 0) + this + }, + repeatType = RepeatType.WEEK, + ) + prepareScenario(alarmData = alarmData) + + alarmRepository.save(alarm, taskId) + + coVerify { alarmLocalDataSource.save(alarmData) } + } @Test - fun `should call save from data source when save is called from repository`() = - runBlocking { - val taskId = 45L - val alarmData = - makeAlarmData( - id = -1, - taskId = taskId, - repeatType = RepeatType.WEEK.ordinal, - nextAlarmDate = "2022-02-23 08:40:00", - insertingDate = null, - ) - val alarm = - makeAlarm( - dateTime = - with(Calendar.getInstance()) { - set(Calendar.YEAR, 2022) - set(Calendar.MONTH, 1) - set(Calendar.HOUR_OF_DAY, 8) - set(Calendar.DAY_OF_MONTH, 23) - set(Calendar.MINUTE, 40) - set(Calendar.SECOND, 0) - set(Calendar.MILLISECOND, 0) - this - }, - repeatType = RepeatType.WEEK, - ) - prepareScenario(alarmData = alarmData) - - alarmRepository.save(alarm, taskId) - - coVerify { alarmLocalDataSource.save(alarmData) } - } + fun `should map alarms by task ids`() = runBlocking { + val alarmDataA = makeAlarmData(taskId = 1L) + val alarmDataB = makeAlarmData(taskId = 2L) + val alarm = makeAlarm() + coEvery { alarmLocalDataSource.getAlarmsByTaskIds(listOf(1L, 2L)) } returns + Result.success(listOf(alarmDataA, alarmDataB)) + coEvery { alarmDataToAlarmMapper.map(alarmDataA) } returns alarm + coEvery { alarmDataToAlarmMapper.map(alarmDataB) } returns alarm + + val result = alarmRepository.getAlarmsByTaskIds(listOf(1L, 2L)).getOrThrow() + + assertEquals(mapOf(1L to alarm, 2L to alarm), result) + coVerify { alarmLocalDataSource.getAlarmsByTaskIds(listOf(1L, 2L)) } + } private fun prepareScenario( alarm: Alarm = makeAlarm(), alarmData: AlarmData = makeAlarmData(), alarmDataResult: Result = Result.success(makeAlarmData()), + alarmDataListResult: Result> = Result.success(listOf(makeAlarmData())), deleteByTaskResult: Result = Result.success(Unit), saveResult: Result = Result.success(42L), ) { coEvery { alarmLocalDataSource.getAlarmByTask(any()) } returns alarmDataResult + coEvery { alarmLocalDataSource.getAlarmsByTaskIds(any()) } returns alarmDataListResult coEvery { alarmLocalDataSource.deleteByTask(any()) } returns deleteByTaskResult coEvery { alarmLocalDataSource.save(any()) } returns saveResult coEvery { alarmDataToAlarmMapper.map(any()) } returns alarm diff --git a/feature/alarm/impl/src/test/java/br/com/sailboat/todozy/feature/alarm/impl/domain/usecase/CancelAlarmScheduleUseCaseImplTest.kt b/feature/alarm/impl/src/test/java/br/com/sailboat/todozy/feature/alarm/impl/domain/usecase/CancelAlarmScheduleUseCaseImplTest.kt index 9c183394..c6418129 100644 --- a/feature/alarm/impl/src/test/java/br/com/sailboat/todozy/feature/alarm/impl/domain/usecase/CancelAlarmScheduleUseCaseImplTest.kt +++ b/feature/alarm/impl/src/test/java/br/com/sailboat/todozy/feature/alarm/impl/domain/usecase/CancelAlarmScheduleUseCaseImplTest.kt @@ -13,11 +13,10 @@ internal class CancelAlarmScheduleUseCaseImplTest { private val cancelAlarmScheduleUseCase = CancelAlarmScheduleUseCaseImpl(alarmManagerService) @Test - fun `should cancel alarm from alarmManagerService`() = - runBlocking { - cancelAlarmScheduleUseCase(45) + fun `should cancel alarm from alarmManagerService`() = runBlocking { + cancelAlarmScheduleUseCase(45) - coVerify { alarmManagerService.cancelAlarm(45) } - confirmVerified(alarmManagerService) - } + coVerify { alarmManagerService.cancelAlarm(45) } + confirmVerified(alarmManagerService) + } } diff --git a/feature/alarm/impl/src/test/java/br/com/sailboat/todozy/feature/alarm/impl/domain/usecase/DeleteAlarmUseCaseImplTest.kt b/feature/alarm/impl/src/test/java/br/com/sailboat/todozy/feature/alarm/impl/domain/usecase/DeleteAlarmUseCaseImplTest.kt index f1cabdda..bb6c780a 100644 --- a/feature/alarm/impl/src/test/java/br/com/sailboat/todozy/feature/alarm/impl/domain/usecase/DeleteAlarmUseCaseImplTest.kt +++ b/feature/alarm/impl/src/test/java/br/com/sailboat/todozy/feature/alarm/impl/domain/usecase/DeleteAlarmUseCaseImplTest.kt @@ -18,20 +18,18 @@ internal class DeleteAlarmUseCaseImplTest { ) @Test - fun `should delete alarm from repository`() = - runBlocking { - deleteAlarmUseCase(45) + fun `should delete alarm from repository`() = runBlocking { + deleteAlarmUseCase(45) - coVerify { repository.deleteAlarmByTask(45) } - confirmVerified(repository) - } + coVerify { repository.deleteAlarmByTask(45) } + confirmVerified(repository) + } @Test - fun `should cancel alarm from cancelAlarmSchedule`() = - runBlocking { - deleteAlarmUseCase(45) + fun `should cancel alarm from cancelAlarmSchedule`() = runBlocking { + deleteAlarmUseCase(45) - coVerify { cancelAlarmScheduleUseCase(45) } - confirmVerified(cancelAlarmScheduleUseCase) - } + coVerify { cancelAlarmScheduleUseCase(45) } + confirmVerified(cancelAlarmScheduleUseCase) + } } diff --git a/feature/alarm/impl/src/test/java/br/com/sailboat/todozy/feature/alarm/impl/domain/usecase/GetAlarmUseCaseImplTest.kt b/feature/alarm/impl/src/test/java/br/com/sailboat/todozy/feature/alarm/impl/domain/usecase/GetAlarmUseCaseImplTest.kt index 5b00554d..404c853f 100644 --- a/feature/alarm/impl/src/test/java/br/com/sailboat/todozy/feature/alarm/impl/domain/usecase/GetAlarmUseCaseImplTest.kt +++ b/feature/alarm/impl/src/test/java/br/com/sailboat/todozy/feature/alarm/impl/domain/usecase/GetAlarmUseCaseImplTest.kt @@ -18,22 +18,21 @@ internal class GetAlarmUseCaseImplTest { private val getAlarmUseCase = GetAlarmUseCaseImpl(repository) @Test - fun `should get alarm from repository`() = - runBlocking { - val taskId = 12L - val alarm = - Alarm( - dateTime = Calendar.getInstance().apply { add(Calendar.HOUR_OF_DAY, -1) }, - repeatType = RepeatType.NOT_REPEAT, - ) - val alarmResult = Result.success(alarm) + fun `should get alarm from repository`() = runBlocking { + val taskId = 12L + val alarm = + Alarm( + dateTime = Calendar.getInstance().apply { add(Calendar.HOUR_OF_DAY, -1) }, + repeatType = RepeatType.NOT_REPEAT, + ) + val alarmResult = Result.success(alarm) - coEvery { repository.getAlarmByTaskId(any()) } returns alarmResult + coEvery { repository.getAlarmByTaskId(any()) } returns alarmResult - val result = getAlarmUseCase(taskId) + val result = getAlarmUseCase(taskId) - coVerify { repository.getAlarmByTaskId(taskId) } - confirmVerified(repository) - assertEquals(result, alarmResult) - } + coVerify { repository.getAlarmByTaskId(taskId) } + confirmVerified(repository) + assertEquals(result, alarmResult) + } } diff --git a/feature/alarm/impl/src/test/java/br/com/sailboat/todozy/feature/alarm/impl/domain/usecase/GetNextAlarmUseCaseImplTest.kt b/feature/alarm/impl/src/test/java/br/com/sailboat/todozy/feature/alarm/impl/domain/usecase/GetNextAlarmUseCaseImplTest.kt index 07d5da69..2cbc1d72 100644 --- a/feature/alarm/impl/src/test/java/br/com/sailboat/todozy/feature/alarm/impl/domain/usecase/GetNextAlarmUseCaseImplTest.kt +++ b/feature/alarm/impl/src/test/java/br/com/sailboat/todozy/feature/alarm/impl/domain/usecase/GetNextAlarmUseCaseImplTest.kt @@ -11,122 +11,114 @@ internal class GetNextAlarmUseCaseImplTest { private val getNextAlarmUseCase = GetNextAlarmUseCaseImpl() @Test - fun `should get next valid alarm when repeatType is SECOND`() = - runBlocking { - val dateTime = Calendar.getInstance() + fun `should get next valid alarm when repeatType is SECOND`() = runBlocking { + val dateTime = Calendar.getInstance() - val result = getNextAlarmUseCase(Alarm(dateTime = dateTime, repeatType = RepeatType.SECOND)) + val result = getNextAlarmUseCase(Alarm(dateTime = dateTime, repeatType = RepeatType.SECOND)) - val expected = - Alarm( - dateTime = dateTime.apply { add(Calendar.SECOND, 1) }, - repeatType = RepeatType.SECOND, - ) - assertEquals(expected, result) - } + val expected = + Alarm( + dateTime = dateTime.apply { add(Calendar.SECOND, 1) }, + repeatType = RepeatType.SECOND, + ) + assertEquals(expected, result) + } @Test - fun `should get next valid alarm when repeatType is MINUTE`() = - runBlocking { - val dateTime = Calendar.getInstance() + fun `should get next valid alarm when repeatType is MINUTE`() = runBlocking { + val dateTime = Calendar.getInstance() - val result = getNextAlarmUseCase(Alarm(dateTime = dateTime, repeatType = RepeatType.MINUTE)) + val result = getNextAlarmUseCase(Alarm(dateTime = dateTime, repeatType = RepeatType.MINUTE)) - val expected = - Alarm( - dateTime = dateTime.apply { add(Calendar.MINUTE, 1) }, - repeatType = RepeatType.MINUTE, - ) - assertEquals(expected, result) - } + val expected = + Alarm( + dateTime = dateTime.apply { add(Calendar.MINUTE, 1) }, + repeatType = RepeatType.MINUTE, + ) + assertEquals(expected, result) + } @Test - fun `should get next valid alarm when repeatType is HOUR`() = - runBlocking { - val dateTime = Calendar.getInstance() + fun `should get next valid alarm when repeatType is HOUR`() = runBlocking { + val dateTime = Calendar.getInstance() - val result = getNextAlarmUseCase(Alarm(dateTime = dateTime, repeatType = RepeatType.HOUR)) + val result = getNextAlarmUseCase(Alarm(dateTime = dateTime, repeatType = RepeatType.HOUR)) - val expected = - Alarm( - dateTime = dateTime.apply { add(Calendar.HOUR, 1) }, - repeatType = RepeatType.HOUR, - ) - assertEquals(expected, result) - } + val expected = + Alarm( + dateTime = dateTime.apply { add(Calendar.HOUR, 1) }, + repeatType = RepeatType.HOUR, + ) + assertEquals(expected, result) + } @Test - fun `should get next valid alarm when repeatType is DAY`() = - runBlocking { - val dateTime = Calendar.getInstance() + fun `should get next valid alarm when repeatType is DAY`() = runBlocking { + val dateTime = Calendar.getInstance() - val result = getNextAlarmUseCase(Alarm(dateTime = dateTime, repeatType = RepeatType.DAY)) + val result = getNextAlarmUseCase(Alarm(dateTime = dateTime, repeatType = RepeatType.DAY)) - val expected = - Alarm( - dateTime = dateTime.apply { add(Calendar.DATE, 1) }, - repeatType = RepeatType.DAY, - ) - assertEquals(expected, result) - } + val expected = + Alarm( + dateTime = dateTime.apply { add(Calendar.DATE, 1) }, + repeatType = RepeatType.DAY, + ) + assertEquals(expected, result) + } @Test - fun `should get next valid alarm when repeatType is WEEK`() = - runBlocking { - val dateTime = Calendar.getInstance() + fun `should get next valid alarm when repeatType is WEEK`() = runBlocking { + val dateTime = Calendar.getInstance() - val result = getNextAlarmUseCase(Alarm(dateTime = dateTime, repeatType = RepeatType.WEEK)) + val result = getNextAlarmUseCase(Alarm(dateTime = dateTime, repeatType = RepeatType.WEEK)) - val expected = - Alarm( - dateTime = dateTime.apply { add(Calendar.WEEK_OF_YEAR, 1) }, - repeatType = RepeatType.WEEK, - ) - assertEquals(expected, result) - } + val expected = + Alarm( + dateTime = dateTime.apply { add(Calendar.WEEK_OF_YEAR, 1) }, + repeatType = RepeatType.WEEK, + ) + assertEquals(expected, result) + } @Test - fun `should get next valid alarm when repeatType is MONTH`() = - runBlocking { - val dateTime = Calendar.getInstance() + fun `should get next valid alarm when repeatType is MONTH`() = runBlocking { + val dateTime = Calendar.getInstance() - val result = getNextAlarmUseCase(Alarm(dateTime = dateTime, repeatType = RepeatType.MONTH)) + val result = getNextAlarmUseCase(Alarm(dateTime = dateTime, repeatType = RepeatType.MONTH)) - val expected = - Alarm( - dateTime = dateTime.apply { add(Calendar.MONTH, 1) }, - repeatType = RepeatType.MONTH, - ) - assertEquals(expected, result) - } + val expected = + Alarm( + dateTime = dateTime.apply { add(Calendar.MONTH, 1) }, + repeatType = RepeatType.MONTH, + ) + assertEquals(expected, result) + } @Test - fun `should get next valid alarm when repeatType is YEAR`() = - runBlocking { - val dateTime = Calendar.getInstance() + fun `should get next valid alarm when repeatType is YEAR`() = runBlocking { + val dateTime = Calendar.getInstance() - val result = getNextAlarmUseCase(Alarm(dateTime = dateTime, repeatType = RepeatType.YEAR)) + val result = getNextAlarmUseCase(Alarm(dateTime = dateTime, repeatType = RepeatType.YEAR)) - val expected = - Alarm( - dateTime = dateTime.apply { add(Calendar.YEAR, 1) }, - repeatType = RepeatType.YEAR, - ) - assertEquals(expected, result) - } + val expected = + Alarm( + dateTime = dateTime.apply { add(Calendar.YEAR, 1) }, + repeatType = RepeatType.YEAR, + ) + assertEquals(expected, result) + } @Test - fun `should get the same alarm when repeatType is NOT_REPEAT`() = - runBlocking { - val dateTime = Calendar.getInstance() - - val result = getNextAlarmUseCase(Alarm(dateTime = dateTime, repeatType = RepeatType.NOT_REPEAT)) - - val expected = - Alarm( - dateTime = dateTime, - repeatType = RepeatType.NOT_REPEAT, - ) - assertEquals(expected, result) - } + fun `should get the same alarm when repeatType is NOT_REPEAT`() = runBlocking { + val dateTime = Calendar.getInstance() + + val result = getNextAlarmUseCase(Alarm(dateTime = dateTime, repeatType = RepeatType.NOT_REPEAT)) + + val expected = + Alarm( + dateTime = dateTime, + repeatType = RepeatType.NOT_REPEAT, + ) + assertEquals(expected, result) + } } diff --git a/feature/alarm/impl/src/test/java/br/com/sailboat/todozy/feature/alarm/impl/domain/usecase/SaveAlarmUseCaseImplTest.kt b/feature/alarm/impl/src/test/java/br/com/sailboat/todozy/feature/alarm/impl/domain/usecase/SaveAlarmUseCaseImplTest.kt index 1f1bfb02..9034b4b9 100644 --- a/feature/alarm/impl/src/test/java/br/com/sailboat/todozy/feature/alarm/impl/domain/usecase/SaveAlarmUseCaseImplTest.kt +++ b/feature/alarm/impl/src/test/java/br/com/sailboat/todozy/feature/alarm/impl/domain/usecase/SaveAlarmUseCaseImplTest.kt @@ -26,29 +26,26 @@ internal class SaveAlarmUseCaseImplTest { private val alarm = Alarm(dateTime = Calendar.getInstance(), repeatType = RepeatType.WEEK) @Test - fun `should save alarm on repository`() = - runBlocking { - saveAlarmUseCase(alarm, taskId) + fun `should save alarm on repository`() = runBlocking { + saveAlarmUseCase(alarm, taskId) - coVerify { repository.save(alarm, taskId) } - confirmVerified(repository) - } + coVerify { repository.save(alarm, taskId) } + confirmVerified(repository) + } @Test - fun `should cancel current alarm schedule when saving`() = - runBlocking { - saveAlarmUseCase(alarm, taskId) + fun `should cancel current alarm schedule when saving`() = runBlocking { + saveAlarmUseCase(alarm, taskId) - coVerify { cancelAlarmScheduleUseCase(taskId) } - confirmVerified(cancelAlarmScheduleUseCase) - } + coVerify { cancelAlarmScheduleUseCase(taskId) } + confirmVerified(cancelAlarmScheduleUseCase) + } @Test - fun `should schedule alarm when saving`() = - runBlocking { - saveAlarmUseCase(alarm, taskId) + fun `should schedule alarm when saving`() = runBlocking { + saveAlarmUseCase(alarm, taskId) - coVerify { scheduleAlarmUseCase(alarm, taskId) } - confirmVerified(scheduleAlarmUseCase) - } + coVerify { scheduleAlarmUseCase(alarm, taskId) } + confirmVerified(scheduleAlarmUseCase) + } } diff --git a/feature/alarm/impl/src/test/java/br/com/sailboat/todozy/feature/alarm/impl/domain/usecase/ScheduleAlarmUpdatesTest.kt b/feature/alarm/impl/src/test/java/br/com/sailboat/todozy/feature/alarm/impl/domain/usecase/ScheduleAlarmUpdatesTest.kt index 00753f32..cd21d663 100644 --- a/feature/alarm/impl/src/test/java/br/com/sailboat/todozy/feature/alarm/impl/domain/usecase/ScheduleAlarmUpdatesTest.kt +++ b/feature/alarm/impl/src/test/java/br/com/sailboat/todozy/feature/alarm/impl/domain/usecase/ScheduleAlarmUpdatesTest.kt @@ -14,20 +14,19 @@ internal class ScheduleAlarmUpdatesTest { private val scheduleAlarmUpdatesUseCase = ScheduleAlarmUpdatesUseCaseImpl(alarmManagerService) @Test - fun `should schedule alarm updates to the first hour of tomorrow on alarmManagerService`() = - runBlocking { - val calendar = - Calendar.getInstance().apply { - add(Calendar.DAY_OF_YEAR, 1) - set(Calendar.HOUR_OF_DAY, 0) - set(Calendar.MINUTE, 0) - set(Calendar.SECOND, 0) - set(Calendar.MILLISECOND, 0) - } + fun `should schedule alarm updates to the first hour of tomorrow on alarmManagerService`() = runBlocking { + val calendar = + Calendar.getInstance().apply { + add(Calendar.DAY_OF_YEAR, 1) + set(Calendar.HOUR_OF_DAY, 0) + set(Calendar.MINUTE, 0) + set(Calendar.SECOND, 0) + set(Calendar.MILLISECOND, 0) + } - scheduleAlarmUpdatesUseCase() + scheduleAlarmUpdatesUseCase() - coVerify { alarmManagerService.scheduleAlarmUpdates(calendar) } - confirmVerified(alarmManagerService) - } + coVerify { alarmManagerService.scheduleAlarmUpdates(calendar) } + confirmVerified(alarmManagerService) + } } diff --git a/feature/alarm/impl/src/test/java/br/com/sailboat/todozy/feature/alarm/impl/domain/usecase/ScheduleAlarmUseCaseImplTest.kt b/feature/alarm/impl/src/test/java/br/com/sailboat/todozy/feature/alarm/impl/domain/usecase/ScheduleAlarmUseCaseImplTest.kt index f38777c8..6efba72b 100644 --- a/feature/alarm/impl/src/test/java/br/com/sailboat/todozy/feature/alarm/impl/domain/usecase/ScheduleAlarmUseCaseImplTest.kt +++ b/feature/alarm/impl/src/test/java/br/com/sailboat/todozy/feature/alarm/impl/domain/usecase/ScheduleAlarmUseCaseImplTest.kt @@ -16,14 +16,13 @@ internal class ScheduleAlarmUseCaseImplTest { private val scheduleAlarmUseCase = ScheduleAlarmUseCaseImpl(alarmManagerService) @Test - fun `should schedule alarm on alarmManagerService`() = - runBlocking { - val taskId = 45L - val alarm = Alarm(dateTime = Calendar.getInstance(), repeatType = RepeatType.WEEK) + fun `should schedule alarm on alarmManagerService`() = runBlocking { + val taskId = 45L + val alarm = Alarm(dateTime = Calendar.getInstance(), repeatType = RepeatType.WEEK) - scheduleAlarmUseCase(alarm, taskId) + scheduleAlarmUseCase(alarm, taskId) - coVerify { alarmManagerService.scheduleAlarm(alarm.dateTime, taskId) } - confirmVerified(alarmManagerService) - } + coVerify { alarmManagerService.scheduleAlarm(alarm.dateTime, taskId) } + confirmVerified(alarmManagerService) + } } diff --git a/feature/alarm/impl/src/test/java/br/com/sailboat/todozy/feature/alarm/impl/domain/usecase/ScheduleAllAlarmsUseCaseImplTest.kt b/feature/alarm/impl/src/test/java/br/com/sailboat/todozy/feature/alarm/impl/domain/usecase/ScheduleAllAlarmsUseCaseImplTest.kt index 7f98dedc..545bc033 100644 --- a/feature/alarm/impl/src/test/java/br/com/sailboat/todozy/feature/alarm/impl/domain/usecase/ScheduleAllAlarmsUseCaseImplTest.kt +++ b/feature/alarm/impl/src/test/java/br/com/sailboat/todozy/feature/alarm/impl/domain/usecase/ScheduleAllAlarmsUseCaseImplTest.kt @@ -28,88 +28,84 @@ internal class ScheduleAllAlarmsUseCaseImplTest { ) @Test - fun `should get tasks with alarms`() = - runBlocking { - prepareScenario() + fun `should get tasks with alarms`() = runBlocking { + prepareScenario() - scheduleAllAlarmsUseCase() + scheduleAllAlarmsUseCase() - coVerify { getTasksUseCase(TaskFilter(category = TaskCategory.WITH_ALARMS)) } - confirmVerified(getTasksUseCase) - } + coVerify { getTasksUseCase(TaskFilter(category = TaskCategory.WITH_ALARMS)) } + confirmVerified(getTasksUseCase) + } @Test - fun `should schedule next valid alarm when alarm is before now`() = - runBlocking { - val alarm = - Alarm( - dateTime = Calendar.getInstance().apply { add(Calendar.HOUR_OF_DAY, -1) }, - repeatType = RepeatType.WEEK, - ) - val task = - Task( - id = 42L, - name = "Task 1", - notes = "Some notes", - alarm = alarm, - ) - prepareScenario(tasksResult = Result.success(listOf(task))) - - scheduleAllAlarmsUseCase() - - coVerify { getTasksUseCase(TaskFilter(category = TaskCategory.WITH_ALARMS)) } - coVerify { getNextAlarmUseCase(alarm) } - confirmVerified(getTasksUseCase) - confirmVerified(getNextAlarmUseCase) - } + fun `should schedule next valid alarm when alarm is before now`() = runBlocking { + val alarm = + Alarm( + dateTime = Calendar.getInstance().apply { add(Calendar.HOUR_OF_DAY, -1) }, + repeatType = RepeatType.WEEK, + ) + val task = + Task( + id = 42L, + name = "Task 1", + notes = "Some notes", + alarm = alarm, + ) + prepareScenario(tasksResult = Result.success(listOf(task))) + + scheduleAllAlarmsUseCase() + + coVerify { getTasksUseCase(TaskFilter(category = TaskCategory.WITH_ALARMS)) } + coVerify { getNextAlarmUseCase(alarm) } + confirmVerified(getTasksUseCase) + confirmVerified(getNextAlarmUseCase) + } @Test - fun `should schedule alarm`() = - runBlocking { - val alarm = - Alarm( - dateTime = Calendar.getInstance().apply { add(Calendar.HOUR_OF_DAY, 1) }, - repeatType = RepeatType.WEEK, - ) - val task = - Task( - id = 42L, - name = "Task 1", - notes = "Some notes", - alarm = alarm, - ) - prepareScenario(tasksResult = Result.success(listOf(task))) - - scheduleAllAlarmsUseCase() - - coVerify { getTasksUseCase(TaskFilter(category = TaskCategory.WITH_ALARMS)) } - coVerify { scheduleAlarmUseCase(alarm, 42L) } - confirmVerified(getTasksUseCase) - confirmVerified(scheduleAlarmUseCase) - } + fun `should schedule alarm`() = runBlocking { + val alarm = + Alarm( + dateTime = Calendar.getInstance().apply { add(Calendar.HOUR_OF_DAY, 1) }, + repeatType = RepeatType.WEEK, + ) + val task = + Task( + id = 42L, + name = "Task 1", + notes = "Some notes", + alarm = alarm, + ) + prepareScenario(tasksResult = Result.success(listOf(task))) + + scheduleAllAlarmsUseCase() + + coVerify { getTasksUseCase(TaskFilter(category = TaskCategory.WITH_ALARMS)) } + coVerify { scheduleAlarmUseCase(alarm, 42L) } + confirmVerified(getTasksUseCase) + confirmVerified(scheduleAlarmUseCase) + } @Test - fun `should not schedule alarm when alarm is before now`() = - runBlocking { - val alarm = - Alarm( - dateTime = Calendar.getInstance().apply { add(Calendar.HOUR_OF_DAY, -1) }, - repeatType = RepeatType.NOT_REPEAT, - ) - val task = - Task( - id = 42L, - name = "Task 1", - notes = "Some notes", - alarm = alarm, - ) - prepareScenario(tasksResult = Result.success(listOf(task))) - - scheduleAllAlarmsUseCase() - - coVerify(exactly = 0) { scheduleAlarmUseCase(alarm, 42L) } - confirmVerified(scheduleAlarmUseCase) - } + fun `should not schedule alarm when alarm is before now`() = runBlocking { + val alarm = + Alarm( + dateTime = Calendar.getInstance().apply { add(Calendar.HOUR_OF_DAY, -1) }, + repeatType = RepeatType.NOT_REPEAT, + ) + val task = + Task( + id = 42L, + name = "Task 1", + notes = "Some notes", + alarm = alarm, + ) + prepareScenario(tasksResult = Result.success(listOf(task))) + + scheduleAllAlarmsUseCase() + + coVerify(exactly = 0) { scheduleAlarmUseCase(alarm, 42L) } + confirmVerified(scheduleAlarmUseCase) + } private fun prepareScenario( tasksResult: Result> = diff --git a/feature/alarm/public/src/main/java/br/com/sailboat/todozy/feature/alarm/domain/repository/AlarmRepository.kt b/feature/alarm/public/src/main/java/br/com/sailboat/todozy/feature/alarm/domain/repository/AlarmRepository.kt index 5c8c0311..a5762a59 100644 --- a/feature/alarm/public/src/main/java/br/com/sailboat/todozy/feature/alarm/domain/repository/AlarmRepository.kt +++ b/feature/alarm/public/src/main/java/br/com/sailboat/todozy/feature/alarm/domain/repository/AlarmRepository.kt @@ -4,6 +4,7 @@ import br.com.sailboat.todozy.domain.model.Alarm interface AlarmRepository { suspend fun getAlarmByTaskId(taskId: Long): Result + suspend fun getAlarmsByTaskIds(taskIds: List): Result> suspend fun deleteAlarmByTask(taskId: Long): Result suspend fun save( alarm: Alarm, diff --git a/feature/settings/impl/src/main/java/br/com/sailboat/todozy/feature/settings/impl/domain/usecase/CheckAndSetUpInitialSettingsUseCaseImpl.kt b/feature/settings/impl/src/main/java/br/com/sailboat/todozy/feature/settings/impl/domain/usecase/CheckAndSetUpInitialSettingsUseCaseImpl.kt index 31633232..147ce092 100644 --- a/feature/settings/impl/src/main/java/br/com/sailboat/todozy/feature/settings/impl/domain/usecase/CheckAndSetUpInitialSettingsUseCaseImpl.kt +++ b/feature/settings/impl/src/main/java/br/com/sailboat/todozy/feature/settings/impl/domain/usecase/CheckAndSetUpInitialSettingsUseCaseImpl.kt @@ -8,15 +8,14 @@ internal class CheckAndSetUpInitialSettingsUseCaseImpl( private val repository: SettingsRepository, private val scheduleAlarmUpdatesUseCase: ScheduleAlarmUpdatesUseCase, ) : CheckAndSetUpInitialSettingsUseCase { - override suspend operator fun invoke() = - with(repository) { - if (isFirstTimeLaunchingApp().not()) { - return - } - - setFirstTimeLaunchingApp(false) - setAlarmVibrate(true) - setDefaultAlarmTone() - scheduleAlarmUpdatesUseCase() + override suspend operator fun invoke() = with(repository) { + if (isFirstTimeLaunchingApp().not()) { + return } + + setFirstTimeLaunchingApp(false) + setAlarmVibrate(true) + setDefaultAlarmTone() + scheduleAlarmUpdatesUseCase() + } } diff --git a/feature/settings/impl/src/main/java/br/com/sailboat/todozy/feature/settings/impl/presentation/SettingsFragment.kt b/feature/settings/impl/src/main/java/br/com/sailboat/todozy/feature/settings/impl/presentation/SettingsFragment.kt index 6127007f..74b89136 100644 --- a/feature/settings/impl/src/main/java/br/com/sailboat/todozy/feature/settings/impl/presentation/SettingsFragment.kt +++ b/feature/settings/impl/src/main/java/br/com/sailboat/todozy/feature/settings/impl/presentation/SettingsFragment.kt @@ -87,19 +87,17 @@ internal class SettingsFragment : Fragment() { activity?.run { aboutNavigator.navigateToAbout(this) } } - private fun initToolbar() = - with(binding) { - appbar.toolbar.setTitle(UiR.string.settings) - appbar.toolbar.setNavigationIcon(UiR.drawable.ic_arrow_back_white_24dp) - appbar.toolbar.setNavigationOnClickListener { requireActivity().onBackPressedDispatcher.onBackPressed() } - } + private fun initToolbar() = with(binding) { + appbar.toolbar.setTitle(UiR.string.settings) + appbar.toolbar.setNavigationIcon(UiR.drawable.ic_arrow_back_white_24dp) + appbar.toolbar.setNavigationOnClickListener { requireActivity().onBackPressedDispatcher.onBackPressed() } + } - private fun initToneViews() = - activity?.run { - binding.llSettingsTone.setSafeClickListener { - viewModel.dispatchViewIntent(SettingsViewIntent.OnClickMenuAlarmTone) - } + private fun initToneViews() = activity?.run { + binding.llSettingsTone.setSafeClickListener { + viewModel.dispatchViewIntent(SettingsViewIntent.OnClickMenuAlarmTone) } + } private fun initAbout() { binding.tvSettingsAbout.setSafeClickListener { @@ -107,15 +105,13 @@ internal class SettingsFragment : Fragment() { } } - private fun initCheckBoxVibrate() = - activity?.run { - binding.cbSettingsVibrate.setOnCheckedChangeListener { _, isChecked -> - viewModel.dispatchViewIntent(SettingsViewIntent.OnClickVibrateAlarm(isChecked)) - } + private fun initCheckBoxVibrate() = activity?.run { + binding.cbSettingsVibrate.setOnCheckedChangeListener { _, isChecked -> + viewModel.dispatchViewIntent(SettingsViewIntent.OnClickVibrateAlarm(isChecked)) } + } - private fun initToneVibrate() = - with(binding) { - flSettingsVibrate.setSafeClickListener { cbSettingsVibrate.performClick() } - } + private fun initToneVibrate() = with(binding) { + flSettingsVibrate.setSafeClickListener { cbSettingsVibrate.performClick() } + } } diff --git a/feature/settings/impl/src/main/java/br/com/sailboat/todozy/feature/settings/impl/presentation/viewmodel/SettingsViewModel.kt b/feature/settings/impl/src/main/java/br/com/sailboat/todozy/feature/settings/impl/presentation/viewmodel/SettingsViewModel.kt index ab2fb911..a63e9dba 100644 --- a/feature/settings/impl/src/main/java/br/com/sailboat/todozy/feature/settings/impl/presentation/viewmodel/SettingsViewModel.kt +++ b/feature/settings/impl/src/main/java/br/com/sailboat/todozy/feature/settings/impl/presentation/viewmodel/SettingsViewModel.kt @@ -25,11 +25,10 @@ internal class SettingsViewModel( } } - private fun onStart() = - viewModelScope.launch { - viewState.alarmSound.value = getAlarmSoundSettingUseCase() - viewState.vibrate.value = getAlarmVibrateSettingUseCase() - } + private fun onStart() = viewModelScope.launch { + viewState.alarmSound.value = getAlarmSoundSettingUseCase() + viewState.vibrate.value = getAlarmVibrateSettingUseCase() + } private fun onClickMenuAlarmTone() { val alarmSound = viewState.alarmSound.value diff --git a/feature/settings/impl/src/test/java/br/com/sailboat/todozy/feature/settings/impl/data/repository/SettingsRepositoryImplTest.kt b/feature/settings/impl/src/test/java/br/com/sailboat/todozy/feature/settings/impl/data/repository/SettingsRepositoryImplTest.kt index 66113552..172fdb0a 100644 --- a/feature/settings/impl/src/test/java/br/com/sailboat/todozy/feature/settings/impl/data/repository/SettingsRepositoryImplTest.kt +++ b/feature/settings/impl/src/test/java/br/com/sailboat/todozy/feature/settings/impl/data/repository/SettingsRepositoryImplTest.kt @@ -22,79 +22,72 @@ internal class SettingsRepositoryImplTest { ) @Test - fun `should call setAlarmTone from data source when called from repository`() = - runBlocking { - val alarmTone: Uri = mockk() - prepareScenario() + fun `should call setAlarmTone from data source when called from repository`() = runBlocking { + val alarmTone: Uri = mockk() + prepareScenario() - settingsRepository.setAlarmTone(alarmTone) + settingsRepository.setAlarmTone(alarmTone) - coVerify { settingsLocalDataSource.setAlarmTone(alarmTone) } - } + coVerify { settingsLocalDataSource.setAlarmTone(alarmTone) } + } @Test - fun `should call setAlarmVibrate from data source when called from repository`() = - runBlocking { - prepareScenario() + fun `should call setAlarmVibrate from data source when called from repository`() = runBlocking { + prepareScenario() - settingsRepository.setAlarmVibrate(true) + settingsRepository.setAlarmVibrate(true) - coVerify { settingsLocalDataSource.setAlarmVibrate(true) } - } + coVerify { settingsLocalDataSource.setAlarmVibrate(true) } + } @Test - fun `should call setDefaultAlarmTone from data source when called from repository`() = - runBlocking { - prepareScenario() + fun `should call setDefaultAlarmTone from data source when called from repository`() = runBlocking { + prepareScenario() - settingsRepository.setDefaultAlarmTone() + settingsRepository.setDefaultAlarmTone() - coVerify { settingsLocalDataSource.setDefaultAlarmTone() } - } + coVerify { settingsLocalDataSource.setDefaultAlarmTone() } + } @Test - fun `should call setFirstTimeLaunchingApp from data source when called from repository`() = - runBlocking { - prepareScenario() + fun `should call setFirstTimeLaunchingApp from data source when called from repository`() = runBlocking { + prepareScenario() - settingsRepository.setFirstTimeLaunchingApp(true) + settingsRepository.setFirstTimeLaunchingApp(true) - coVerify { settingsLocalDataSource.setFirstTimeLaunchingApp(true) } - } + coVerify { settingsLocalDataSource.setFirstTimeLaunchingApp(true) } + } @Test - fun `should call getAlarmTone from data source when called from repository`() = - runBlocking { - val alarmTone: Uri = mockk() - prepareScenario(alarmTone = alarmTone) + fun `should call getAlarmTone from data source when called from repository`() = runBlocking { + val alarmTone: Uri = mockk() + prepareScenario(alarmTone = alarmTone) - val result = settingsRepository.getAlarmTone() + val result = settingsRepository.getAlarmTone() - coVerify { settingsLocalDataSource.getAlarmTone() } - assertEquals(alarmTone, result) - } + coVerify { settingsLocalDataSource.getAlarmTone() } + assertEquals(alarmTone, result) + } @Test - fun `should call getAlarmVibrate from data source when called from repository`() = - runBlocking { - prepareScenario(vibrate = false) + fun `should call getAlarmVibrate from data source when called from repository`() = runBlocking { + prepareScenario(vibrate = false) - val result = settingsRepository.getAlarmVibrate() + val result = settingsRepository.getAlarmVibrate() - coVerify { settingsLocalDataSource.getAlarmVibrate() } - assertFalse { result } - } + coVerify { settingsLocalDataSource.getAlarmVibrate() } + assertFalse { result } + } @Test - fun `should call isFirstTimeLaunchingApp from data source when called from repository`() = - runBlocking { - prepareScenario(isFirstTimeLaunchingApp = true) + fun `should call isFirstTimeLaunchingApp from data source when called from repository`() = runBlocking { + prepareScenario(isFirstTimeLaunchingApp = true) - val result = settingsRepository.isFirstTimeLaunchingApp() + val result = settingsRepository.isFirstTimeLaunchingApp() - coVerify { settingsLocalDataSource.isFirstTimeLaunchingApp() } - assertTrue { result } - } + coVerify { settingsLocalDataSource.isFirstTimeLaunchingApp() } + assertTrue { result } + } private fun prepareScenario( alarmTone: Uri? = mockk(), diff --git a/feature/settings/impl/src/test/java/br/com/sailboat/todozy/feature/settings/impl/domain/usecase/CheckAndSetUpInitialSettingsUseCaseImplTest.kt b/feature/settings/impl/src/test/java/br/com/sailboat/todozy/feature/settings/impl/domain/usecase/CheckAndSetUpInitialSettingsUseCaseImplTest.kt index b6bbe062..4eb608ae 100644 --- a/feature/settings/impl/src/test/java/br/com/sailboat/todozy/feature/settings/impl/domain/usecase/CheckAndSetUpInitialSettingsUseCaseImplTest.kt +++ b/feature/settings/impl/src/test/java/br/com/sailboat/todozy/feature/settings/impl/domain/usecase/CheckAndSetUpInitialSettingsUseCaseImplTest.kt @@ -20,69 +20,63 @@ internal class CheckAndSetUpInitialSettingsUseCaseImplTest { ) @Test - fun `should check if it is the first time that the app is launching from repository`() = - runBlocking { - coEvery { repository.isFirstTimeLaunchingApp() } returns true + fun `should check if it is the first time that the app is launching from repository`() = runBlocking { + coEvery { repository.isFirstTimeLaunchingApp() } returns true - checkAndSetUpInitialSettingsUseCase() + checkAndSetUpInitialSettingsUseCase() - coVerify { repository.isFirstTimeLaunchingApp() } - } + coVerify { repository.isFirstTimeLaunchingApp() } + } @Test - fun `should set first time launching app as false when checking from repository`() = - runBlocking { - coEvery { repository.isFirstTimeLaunchingApp() } returns true + fun `should set first time launching app as false when checking from repository`() = runBlocking { + coEvery { repository.isFirstTimeLaunchingApp() } returns true - checkAndSetUpInitialSettingsUseCase() + checkAndSetUpInitialSettingsUseCase() - coVerify { repository.setFirstTimeLaunchingApp(false) } - } + coVerify { repository.setFirstTimeLaunchingApp(false) } + } @Test - fun `should set alarm vibrate as true when it is the first time that the app is launching from repository`() = - runBlocking { - coEvery { repository.isFirstTimeLaunchingApp() } returns true + fun `should set alarm vibrate as true when it is the first time that the app is launching from repository`() = runBlocking { + coEvery { repository.isFirstTimeLaunchingApp() } returns true - checkAndSetUpInitialSettingsUseCase() + checkAndSetUpInitialSettingsUseCase() - coVerify { repository.setAlarmVibrate(true) } - } + coVerify { repository.setAlarmVibrate(true) } + } @Test - fun `should set default alarm when it is the first time that the app is launching from repository`() = - runBlocking { - coEvery { repository.isFirstTimeLaunchingApp() } returns true + fun `should set default alarm when it is the first time that the app is launching from repository`() = runBlocking { + coEvery { repository.isFirstTimeLaunchingApp() } returns true - checkAndSetUpInitialSettingsUseCase() + checkAndSetUpInitialSettingsUseCase() - coVerify { repository.setDefaultAlarmTone() } - } + coVerify { repository.setDefaultAlarmTone() } + } @Test - fun `should schedule alarm update when it is the first time that the app is launching`() = - runBlocking { - coEvery { repository.isFirstTimeLaunchingApp() } returns true + fun `should schedule alarm update when it is the first time that the app is launching`() = runBlocking { + coEvery { repository.isFirstTimeLaunchingApp() } returns true - checkAndSetUpInitialSettingsUseCase() + checkAndSetUpInitialSettingsUseCase() - coVerify { scheduleAlarmUpdatesUseCase() } - confirmVerified(scheduleAlarmUpdatesUseCase) - } + coVerify { scheduleAlarmUpdatesUseCase() } + confirmVerified(scheduleAlarmUpdatesUseCase) + } @Test - fun `should not perform any settings when isFirstTimeLaunchingApp returns false`() = - runBlocking { - coEvery { repository.isFirstTimeLaunchingApp() } returns false - - checkAndSetUpInitialSettingsUseCase() - - coVerify(exactly = 1) { repository.isFirstTimeLaunchingApp() } - coVerify(exactly = 0) { repository.setFirstTimeLaunchingApp(false) } - coVerify(exactly = 0) { repository.setAlarmVibrate(true) } - coVerify(exactly = 0) { repository.setDefaultAlarmTone() } - coVerify(exactly = 0) { scheduleAlarmUpdatesUseCase() } - confirmVerified(repository) - confirmVerified(scheduleAlarmUpdatesUseCase) - } + fun `should not perform any settings when isFirstTimeLaunchingApp returns false`() = runBlocking { + coEvery { repository.isFirstTimeLaunchingApp() } returns false + + checkAndSetUpInitialSettingsUseCase() + + coVerify(exactly = 1) { repository.isFirstTimeLaunchingApp() } + coVerify(exactly = 0) { repository.setFirstTimeLaunchingApp(false) } + coVerify(exactly = 0) { repository.setAlarmVibrate(true) } + coVerify(exactly = 0) { repository.setDefaultAlarmTone() } + coVerify(exactly = 0) { scheduleAlarmUpdatesUseCase() } + confirmVerified(repository) + confirmVerified(scheduleAlarmUpdatesUseCase) + } } diff --git a/feature/settings/impl/src/test/java/br/com/sailboat/todozy/feature/settings/impl/domain/usecase/GetAlarmSoundSettingUseCaseImplTest.kt b/feature/settings/impl/src/test/java/br/com/sailboat/todozy/feature/settings/impl/domain/usecase/GetAlarmSoundSettingUseCaseImplTest.kt index db5bff68..5c480de1 100644 --- a/feature/settings/impl/src/test/java/br/com/sailboat/todozy/feature/settings/impl/domain/usecase/GetAlarmSoundSettingUseCaseImplTest.kt +++ b/feature/settings/impl/src/test/java/br/com/sailboat/todozy/feature/settings/impl/domain/usecase/GetAlarmSoundSettingUseCaseImplTest.kt @@ -13,11 +13,10 @@ internal class GetAlarmSoundSettingUseCaseImplTest { private val getAlarmSoundSettingUseCase = GetAlarmSoundSettingUseCaseImpl(repository) @Test - fun `should get alarm sound setting from repository`() = - runBlocking { - getAlarmSoundSettingUseCase() + fun `should get alarm sound setting from repository`() = runBlocking { + getAlarmSoundSettingUseCase() - coVerify { repository.getAlarmTone() } - confirmVerified(repository) - } + coVerify { repository.getAlarmTone() } + confirmVerified(repository) + } } diff --git a/feature/settings/impl/src/test/java/br/com/sailboat/todozy/feature/settings/impl/domain/usecase/GetAlarmVibrateSettingUseCaseImplTest.kt b/feature/settings/impl/src/test/java/br/com/sailboat/todozy/feature/settings/impl/domain/usecase/GetAlarmVibrateSettingUseCaseImplTest.kt index b88d8374..c2f2c03a 100644 --- a/feature/settings/impl/src/test/java/br/com/sailboat/todozy/feature/settings/impl/domain/usecase/GetAlarmVibrateSettingUseCaseImplTest.kt +++ b/feature/settings/impl/src/test/java/br/com/sailboat/todozy/feature/settings/impl/domain/usecase/GetAlarmVibrateSettingUseCaseImplTest.kt @@ -13,11 +13,10 @@ internal class GetAlarmVibrateSettingUseCaseImplTest { private val getAlarmVibrateSettingUseCase = GetAlarmVibrateSettingUseCaseImpl(repository) @Test - fun `should get alarm vibrate setting from repository`() = - runBlocking { - getAlarmVibrateSettingUseCase() + fun `should get alarm vibrate setting from repository`() = runBlocking { + getAlarmVibrateSettingUseCase() - coVerify { repository.getAlarmVibrate() } - confirmVerified(repository) - } + coVerify { repository.getAlarmVibrate() } + confirmVerified(repository) + } } diff --git a/feature/settings/impl/src/test/java/br/com/sailboat/todozy/feature/settings/impl/domain/usecase/SetAlarmSoundSettingUseCaseImplTest.kt b/feature/settings/impl/src/test/java/br/com/sailboat/todozy/feature/settings/impl/domain/usecase/SetAlarmSoundSettingUseCaseImplTest.kt index e5118e11..9d1c54c9 100644 --- a/feature/settings/impl/src/test/java/br/com/sailboat/todozy/feature/settings/impl/domain/usecase/SetAlarmSoundSettingUseCaseImplTest.kt +++ b/feature/settings/impl/src/test/java/br/com/sailboat/todozy/feature/settings/impl/domain/usecase/SetAlarmSoundSettingUseCaseImplTest.kt @@ -14,12 +14,11 @@ internal class SetAlarmSoundSettingUseCaseImplTest { private val setAlarmSoundSettingUseCase = SetAlarmSoundSettingUseCaseImpl(repository) @Test - fun `should set alarm sound setting from repository`() = - runBlocking { - val uriMock = mockk() - setAlarmSoundSettingUseCase(uriMock) + fun `should set alarm sound setting from repository`() = runBlocking { + val uriMock = mockk() + setAlarmSoundSettingUseCase(uriMock) - coVerify { repository.setAlarmTone(uriMock) } - confirmVerified(repository) - } + coVerify { repository.setAlarmTone(uriMock) } + confirmVerified(repository) + } } diff --git a/feature/settings/impl/src/test/java/br/com/sailboat/todozy/feature/settings/impl/domain/usecase/SetAlarmVibrateSettingUseCaseImplTest.kt b/feature/settings/impl/src/test/java/br/com/sailboat/todozy/feature/settings/impl/domain/usecase/SetAlarmVibrateSettingUseCaseImplTest.kt index fe9381ea..b92d8ade 100644 --- a/feature/settings/impl/src/test/java/br/com/sailboat/todozy/feature/settings/impl/domain/usecase/SetAlarmVibrateSettingUseCaseImplTest.kt +++ b/feature/settings/impl/src/test/java/br/com/sailboat/todozy/feature/settings/impl/domain/usecase/SetAlarmVibrateSettingUseCaseImplTest.kt @@ -13,11 +13,10 @@ internal class SetAlarmVibrateSettingUseCaseImplTest { private val setAlarmVibrateSettingUseCase = SetAlarmVibrateSettingUseCaseImpl(repository) @Test - fun `should get alarm vibrate setting from repository`() = - runBlocking { - setAlarmVibrateSettingUseCase(true) + fun `should get alarm vibrate setting from repository`() = runBlocking { + setAlarmVibrateSettingUseCase(true) - coVerify { repository.setAlarmVibrate(true) } - confirmVerified(repository) - } + coVerify { repository.setAlarmVibrate(true) } + confirmVerified(repository) + } } diff --git a/feature/task-details/impl/src/main/java/br/com/sailboat/todozy/feature/task/details/impl/domain/usecase/DisableTaskUseCaseImpl.kt b/feature/task-details/impl/src/main/java/br/com/sailboat/todozy/feature/task/details/impl/domain/usecase/DisableTaskUseCaseImpl.kt index f47cca28..03330351 100644 --- a/feature/task-details/impl/src/main/java/br/com/sailboat/todozy/feature/task/details/impl/domain/usecase/DisableTaskUseCaseImpl.kt +++ b/feature/task-details/impl/src/main/java/br/com/sailboat/todozy/feature/task/details/impl/domain/usecase/DisableTaskUseCaseImpl.kt @@ -9,11 +9,10 @@ internal class DisableTaskUseCaseImpl( private val taskRepository: TaskRepository, private val deleteAlarmUseCase: DeleteAlarmUseCase, ) : DisableTaskUseCase { - override suspend operator fun invoke(task: Task): Result = - runCatching { - taskRepository.disableTask(task).getOrThrow() - deleteAlarmUseCase(task.id) + override suspend operator fun invoke(task: Task): Result = runCatching { + taskRepository.disableTask(task).getOrThrow() + deleteAlarmUseCase(task.id) - return@runCatching task - } + return@runCatching task + } } diff --git a/feature/task-details/impl/src/main/java/br/com/sailboat/todozy/feature/task/details/impl/domain/usecase/GetTaskMetricsUseCaseImpl.kt b/feature/task-details/impl/src/main/java/br/com/sailboat/todozy/feature/task/details/impl/domain/usecase/GetTaskMetricsUseCaseImpl.kt index 6a303263..5c6a9656 100644 --- a/feature/task-details/impl/src/main/java/br/com/sailboat/todozy/feature/task/details/impl/domain/usecase/GetTaskMetricsUseCaseImpl.kt +++ b/feature/task-details/impl/src/main/java/br/com/sailboat/todozy/feature/task/details/impl/domain/usecase/GetTaskMetricsUseCaseImpl.kt @@ -18,51 +18,48 @@ import java.util.GregorianCalendar internal class GetTaskMetricsUseCaseImpl( private val taskHistoryRepository: TaskHistoryRepository, ) : GetTaskMetricsUseCase { - override suspend operator fun invoke(filter: TaskHistoryFilter) = - runCatching { - coroutineScope { - val done = async { taskHistoryRepository.getTotalOfDoneTasks(filter).getOrThrow() } - val notDone = - async { - taskHistoryRepository.getTotalOfNotDoneTasks(filter).getOrThrow() - } - val consecutiveDone = async { getTotalOfConsecutiveDoneTasks(filter) } + override suspend operator fun invoke(filter: TaskHistoryFilter) = runCatching { + coroutineScope { + val done = async { taskHistoryRepository.getTotalOfDoneTasks(filter).getOrThrow() } + val notDone = + async { + taskHistoryRepository.getTotalOfNotDoneTasks(filter).getOrThrow() + } + val consecutiveDone = async { getTotalOfConsecutiveDoneTasks(filter) } - TaskMetrics(done.await(), notDone.await(), consecutiveDone.await().getOrThrow()) - } + TaskMetrics(done.await(), notDone.await(), consecutiveDone.await().getOrThrow()) } + } - private suspend fun getTotalOfConsecutiveDoneTasks(filter: TaskHistoryFilter): Result = - runCatching { - if (filter.taskId == Entity.NO_ID) { - return@runCatching 0 - } + private suspend fun getTotalOfConsecutiveDoneTasks(filter: TaskHistoryFilter): Result = runCatching { + if (filter.taskId == Entity.NO_ID) { + return@runCatching 0 + } - val history = - taskHistoryRepository - .getHistory(filter) - .getOrThrow() - .sortedByDescending { it.insertingDateMillis() } + val history = + taskHistoryRepository + .getHistory(filter) + .getOrThrow() + .sortedByDescending { it.insertingDateMillis() } - var cont = 0 + var cont = 0 - history.forEach { - if (it.status == TaskStatus.NOT_DONE) { - return@runCatching cont - } - cont++ + history.forEach { + if (it.status == TaskStatus.NOT_DONE) { + return@runCatching cont } - - return@runCatching cont + cont++ } - private fun TaskHistory.insertingDateMillis(): Long = - runCatching { insertingDate.toDateTimeCalendar().timeInMillis } - .getOrElse { - runCatching { - val formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd-HH-mm-ss") - val localDateTime = LocalDateTime.parse(insertingDate, formatter) - GregorianCalendar.from(localDateTime.atZone(ZoneId.systemDefault())).timeInMillis - }.getOrElse { 0L } - } + return@runCatching cont + } + + private fun TaskHistory.insertingDateMillis(): Long = runCatching { insertingDate.toDateTimeCalendar().timeInMillis } + .getOrElse { + runCatching { + val formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd-HH-mm-ss") + val localDateTime = LocalDateTime.parse(insertingDate, formatter) + GregorianCalendar.from(localDateTime.atZone(ZoneId.systemDefault())).timeInMillis + }.getOrElse { 0L } + } } diff --git a/feature/task-details/impl/src/main/java/br/com/sailboat/todozy/feature/task/details/impl/presentation/TaskDetailsFragment.kt b/feature/task-details/impl/src/main/java/br/com/sailboat/todozy/feature/task/details/impl/presentation/TaskDetailsFragment.kt index 6e117ca4..223fc8e0 100644 --- a/feature/task-details/impl/src/main/java/br/com/sailboat/todozy/feature/task/details/impl/presentation/TaskDetailsFragment.kt +++ b/feature/task-details/impl/src/main/java/br/com/sailboat/todozy/feature/task/details/impl/presentation/TaskDetailsFragment.kt @@ -61,13 +61,12 @@ internal class TaskDetailsFragment : Fragment() { } companion object { - fun newInstance(taskId: Long): TaskDetailsFragment = - with(TaskDetailsFragment()) { - val bundle = Bundle() - bundle.putTaskId(taskId) - arguments = bundle - return this - } + fun newInstance(taskId: Long): TaskDetailsFragment = with(TaskDetailsFragment()) { + val bundle = Bundle() + bundle.putTaskId(taskId) + arguments = bundle + return this + } } private lateinit var binding: FrgTaskDetailsBinding diff --git a/feature/task-details/impl/src/main/java/br/com/sailboat/todozy/feature/task/details/impl/presentation/viewmodel/TaskDetailsViewModel.kt b/feature/task-details/impl/src/main/java/br/com/sailboat/todozy/feature/task/details/impl/presentation/viewmodel/TaskDetailsViewModel.kt index c40560dc..10bf2ccf 100644 --- a/feature/task-details/impl/src/main/java/br/com/sailboat/todozy/feature/task/details/impl/presentation/viewmodel/TaskDetailsViewModel.kt +++ b/feature/task-details/impl/src/main/java/br/com/sailboat/todozy/feature/task/details/impl/presentation/viewmodel/TaskDetailsViewModel.kt @@ -62,17 +62,16 @@ internal class TaskDetailsViewModel( viewState.viewAction.value = TaskDetailsViewAction.ConfirmDeleteTask } - private fun onClickConfirmDeleteTask() = - viewModelScope.launch { - try { - val task = getTaskUseCase(viewState.taskId).getOrThrow() - disableTaskUseCase(task).getOrThrow() - viewState.viewAction.value = TaskDetailsViewAction.CloseTaskDetails(success = true) - } catch (e: Exception) { - logService.error(e) - viewState.viewAction.value = TaskDetailsViewAction.ShowErrorLoadingTaskDetails - } + private fun onClickConfirmDeleteTask() = viewModelScope.launch { + try { + val task = getTaskUseCase(viewState.taskId).getOrThrow() + disableTaskUseCase(task).getOrThrow() + viewState.viewAction.value = TaskDetailsViewAction.CloseTaskDetails(success = true) + } catch (e: Exception) { + logService.error(e) + viewState.viewAction.value = TaskDetailsViewAction.ShowErrorLoadingTaskDetails } + } private fun onClickEditTask() { viewState.viewAction.value = TaskDetailsViewAction.NavigateToTaskForm(viewState.taskId) @@ -82,42 +81,41 @@ internal class TaskDetailsViewModel( loadDetails() } - private fun loadDetails() = - viewModelScope.launch { - try { - viewState.loading.postValue(true) - progressCache.clear() - currentAlarm = null + private fun loadDetails() = viewModelScope.launch { + try { + viewState.loading.postValue(true) + progressCache.clear() + currentAlarm = null - val task = getTaskUseCase(viewState.taskId).getOrThrow() - val taskDetails = taskDetailsUiModelFactory.create(task) - currentAlarm = task.alarm - viewState.taskProgressDayOrder.postValue(dayOrderForAlarm(task.alarm)) + val task = getTaskUseCase(viewState.taskId).getOrThrow() + val taskDetails = taskDetailsUiModelFactory.create(task) + currentAlarm = task.alarm + viewState.taskProgressDayOrder.postValue(dayOrderForAlarm(task.alarm)) - viewState.taskDetails.postValue(taskDetails) + viewState.taskDetails.postValue(taskDetails) - shouldShowProgress = false - val taskMetrics: TaskMetrics? = - task.alarm?.run { - shouldShowProgress = RepeatType.isAlarmRepeating(this) - if (shouldShowProgress) { - val taskMetrics = getTaskMetricsUseCase(historyFilterForRange()) - return@run taskMetrics.getOrNull() - } - shouldShowProgress = false - return@run null + shouldShowProgress = false + val taskMetrics: TaskMetrics? = + task.alarm?.run { + shouldShowProgress = RepeatType.isAlarmRepeating(this) + if (shouldShowProgress) { + val taskMetrics = getTaskMetricsUseCase(historyFilterForRange()) + return@run taskMetrics.getOrNull() } + shouldShowProgress = false + return@run null + } - viewState.taskMetrics.postValue(taskMetrics) - loadProgress() - } catch (e: Exception) { - logService.error(e) - viewState.viewAction.value = TaskDetailsViewAction.ShowErrorLoadingTaskDetails - viewState.viewAction.value = TaskDetailsViewAction.CloseTaskDetails(success = false) - } finally { - viewState.loading.postValue(false) - } + viewState.taskMetrics.postValue(taskMetrics) + loadProgress() + } catch (e: Exception) { + logService.error(e) + viewState.viewAction.value = TaskDetailsViewAction.ShowErrorLoadingTaskDetails + viewState.viewAction.value = TaskDetailsViewAction.CloseTaskDetails(success = false) + } finally { + viewState.loading.postValue(false) } + } private fun onSelectProgressRange(range: TaskProgressRange) { if (range == selectedProgressRange) { @@ -174,23 +172,22 @@ internal class TaskDetailsViewModel( } } - private fun loadTaskMetrics() = - viewModelScope.launch { - if (shouldShowProgress.not()) { - viewState.taskMetrics.postValue(null) - return@launch - } + private fun loadTaskMetrics() = viewModelScope.launch { + if (shouldShowProgress.not()) { + viewState.taskMetrics.postValue(null) + return@launch + } - runCatching { - val filter = historyFilterForRange() - getTaskMetricsUseCase(filter).getOrThrow() - }.onSuccess { metrics -> - viewState.taskMetrics.postValue(metrics) - }.onFailure { throwable -> - logService.error(throwable) - viewState.taskMetrics.postValue(null) - } + runCatching { + val filter = historyFilterForRange() + getTaskMetricsUseCase(filter).getOrThrow() + }.onSuccess { metrics -> + viewState.taskMetrics.postValue(metrics) + }.onFailure { throwable -> + logService.error(throwable) + viewState.taskMetrics.postValue(null) } + } private fun historyFilterForRange(): TaskHistoryFilter { if (selectedProgressRange == TaskProgressRange.ALL) { @@ -264,22 +261,20 @@ internal class TaskDetailsViewModel( } } - private fun parseCustomDays(customDays: String?): Set = - customDays - ?.mapNotNull { it.digitToIntOrNull() } - ?.mapNotNull { dayId -> calendarDayToDayOfWeek(dayId) } - ?.toSet() - ?: emptySet() + private fun parseCustomDays(customDays: String?): Set = customDays + ?.mapNotNull { it.digitToIntOrNull() } + ?.mapNotNull { dayId -> calendarDayToDayOfWeek(dayId) } + ?.toSet() + ?: emptySet() - private fun calendarDayToDayOfWeek(dayId: Int): DayOfWeek? = - when (dayId) { - Calendar.SUNDAY -> DayOfWeek.SUNDAY - Calendar.MONDAY -> DayOfWeek.MONDAY - Calendar.TUESDAY -> DayOfWeek.TUESDAY - Calendar.WEDNESDAY -> DayOfWeek.WEDNESDAY - Calendar.THURSDAY -> DayOfWeek.THURSDAY - Calendar.FRIDAY -> DayOfWeek.FRIDAY - Calendar.SATURDAY -> DayOfWeek.SATURDAY - else -> null - } + private fun calendarDayToDayOfWeek(dayId: Int): DayOfWeek? = when (dayId) { + Calendar.SUNDAY -> DayOfWeek.SUNDAY + Calendar.MONDAY -> DayOfWeek.MONDAY + Calendar.TUESDAY -> DayOfWeek.TUESDAY + Calendar.WEDNESDAY -> DayOfWeek.WEDNESDAY + Calendar.THURSDAY -> DayOfWeek.THURSDAY + Calendar.FRIDAY -> DayOfWeek.FRIDAY + Calendar.SATURDAY -> DayOfWeek.SATURDAY + else -> null + } } diff --git a/feature/task-details/impl/src/test/java/br/com/sailboat/todozy/feature/task/details/impl/domain/usecase/DisableTaskUseCaseImplTest.kt b/feature/task-details/impl/src/test/java/br/com/sailboat/todozy/feature/task/details/impl/domain/usecase/DisableTaskUseCaseImplTest.kt index 1bf712bd..527de4ae 100644 --- a/feature/task-details/impl/src/test/java/br/com/sailboat/todozy/feature/task/details/impl/domain/usecase/DisableTaskUseCaseImplTest.kt +++ b/feature/task-details/impl/src/test/java/br/com/sailboat/todozy/feature/task/details/impl/domain/usecase/DisableTaskUseCaseImplTest.kt @@ -19,20 +19,18 @@ internal class DisableTaskUseCaseImplTest { private val task = Task(id = Entity.NO_ID, name = "Task Name", notes = "Some notes") @Test - fun `should disable task from repository`() = - runBlocking { - disableTaskUseCase(task) + fun `should disable task from repository`() = runBlocking { + disableTaskUseCase(task) - coVerify { repository.disableTask(task) } - confirmVerified(repository) - } + coVerify { repository.disableTask(task) } + confirmVerified(repository) + } @Test - fun `should call deleteAlarm when disable task`() = - runBlocking { - disableTaskUseCase(task) + fun `should call deleteAlarm when disable task`() = runBlocking { + disableTaskUseCase(task) - coVerify { deleteAlarmUseCase(task.id) } - confirmVerified(deleteAlarmUseCase) - } + coVerify { deleteAlarmUseCase(task.id) } + confirmVerified(deleteAlarmUseCase) + } } diff --git a/feature/task-details/impl/src/test/java/br/com/sailboat/todozy/feature/task/details/impl/domain/usecase/GetTaskMetricsUseCaseImplTest.kt b/feature/task-details/impl/src/test/java/br/com/sailboat/todozy/feature/task/details/impl/domain/usecase/GetTaskMetricsUseCaseImplTest.kt index 91c06ff1..e2921572 100644 --- a/feature/task-details/impl/src/test/java/br/com/sailboat/todozy/feature/task/details/impl/domain/usecase/GetTaskMetricsUseCaseImplTest.kt +++ b/feature/task-details/impl/src/test/java/br/com/sailboat/todozy/feature/task/details/impl/domain/usecase/GetTaskMetricsUseCaseImplTest.kt @@ -19,34 +19,32 @@ internal class GetTaskMetricsUseCaseImplTest { private val getTaskMetricsUseCase = GetTaskMetricsUseCaseImpl(repository) @Test - fun `should get task metrics from repository`() = - runBlocking { - prepareScenario() + fun `should get task metrics from repository`() = runBlocking { + prepareScenario() - val result = getTaskMetricsUseCase(TaskHistoryFilter(taskId = 22)).getOrNull() + val result = getTaskMetricsUseCase(TaskHistoryFilter(taskId = 22)).getOrNull() - coVerify { repository.getHistory(any()) } - coVerify { repository.getTotalOfDoneTasks(any()) } - coVerify { repository.getTotalOfNotDoneTasks(any()) } + coVerify { repository.getHistory(any()) } + coVerify { repository.getTotalOfDoneTasks(any()) } + coVerify { repository.getTotalOfNotDoneTasks(any()) } - confirmVerified(repository) - assertEquals(TaskMetrics(doneTasks = 10, notDoneTasks = 5, consecutiveDone = 2), result) - } + confirmVerified(repository) + assertEquals(TaskMetrics(doneTasks = 10, notDoneTasks = 5, consecutiveDone = 2), result) + } @Test - fun `should return consecutiveDone 0 from repository when taskId has NO_ID`() = - runBlocking { - prepareScenario() + fun `should return consecutiveDone 0 from repository when taskId has NO_ID`() = runBlocking { + prepareScenario() - val result = getTaskMetricsUseCase(TaskHistoryFilter(taskId = Entity.NO_ID)).getOrNull() + val result = getTaskMetricsUseCase(TaskHistoryFilter(taskId = Entity.NO_ID)).getOrNull() - coVerify(exactly = 0) { repository.getHistory(any()) } - coVerify { repository.getTotalOfDoneTasks(any()) } - coVerify { repository.getTotalOfNotDoneTasks(any()) } + coVerify(exactly = 0) { repository.getHistory(any()) } + coVerify { repository.getTotalOfDoneTasks(any()) } + coVerify { repository.getTotalOfNotDoneTasks(any()) } - confirmVerified(repository) - assertEquals(TaskMetrics(doneTasks = 10, notDoneTasks = 5, consecutiveDone = 0), result) - } + confirmVerified(repository) + assertEquals(TaskMetrics(doneTasks = 10, notDoneTasks = 5, consecutiveDone = 0), result) + } private fun prepareScenario( taskHistoryListResult: Result> = diff --git a/feature/task-details/impl/src/test/java/br/com/sailboat/todozy/feature/task/details/impl/domain/usecase/GetTaskUseCaseImplTest.kt b/feature/task-details/impl/src/test/java/br/com/sailboat/todozy/feature/task/details/impl/domain/usecase/GetTaskUseCaseImplTest.kt index f9ef2e05..d86b1dd7 100644 --- a/feature/task-details/impl/src/test/java/br/com/sailboat/todozy/feature/task/details/impl/domain/usecase/GetTaskUseCaseImplTest.kt +++ b/feature/task-details/impl/src/test/java/br/com/sailboat/todozy/feature/task/details/impl/domain/usecase/GetTaskUseCaseImplTest.kt @@ -16,25 +16,24 @@ internal class GetTaskUseCaseImplTest { private val getTaskUseCase = GetTaskUseCaseImpl(repository) @Test - fun `should get task from repository`() = - runBlocking { - val taskId = 42L - val taskResult: Result = - Result.success( - Task( - id = taskId, - name = "Task Name", - notes = "Some notes", - ), - ) - prepareScenario() + fun `should get task from repository`() = runBlocking { + val taskId = 42L + val taskResult: Result = + Result.success( + Task( + id = taskId, + name = "Task Name", + notes = "Some notes", + ), + ) + prepareScenario() - val result = getTaskUseCase(taskId) + val result = getTaskUseCase(taskId) - coVerify { repository.getTask(taskId) } - confirmVerified(repository) - assertEquals(taskResult, result) - } + coVerify { repository.getTask(taskId) } + confirmVerified(repository) + assertEquals(taskResult, result) + } private fun prepareScenario( taskResult: Result = diff --git a/feature/task-details/impl/src/test/java/br/com/sailboat/todozy/feature/task/details/impl/presentation/factory/TaskDetailsUiModelFactoryTest.kt b/feature/task-details/impl/src/test/java/br/com/sailboat/todozy/feature/task/details/impl/presentation/factory/TaskDetailsUiModelFactoryTest.kt index ef296d61..f4bd461d 100644 --- a/feature/task-details/impl/src/test/java/br/com/sailboat/todozy/feature/task/details/impl/presentation/factory/TaskDetailsUiModelFactoryTest.kt +++ b/feature/task-details/impl/src/test/java/br/com/sailboat/todozy/feature/task/details/impl/presentation/factory/TaskDetailsUiModelFactoryTest.kt @@ -27,47 +27,46 @@ internal class TaskDetailsUiModelFactoryTest { ) @Test - fun `should create task details list when create is called from taskDetailsUiModelFactory`() = - runBlocking { - val task = - Task( - id = 45L, - name = "Task Name", - notes = "Task Notes", - alarm = - Alarm( - dateTime = Calendar.getInstance(), - repeatType = RepeatType.WEEK, - ), - ) - val alarmUiModel = - AlarmUiModel( - date = "07/03/2022", - time = "11:55", - description = "Today, March 7, 2022", - isCustom = false, - shouldRepeat = true, - customDays = null, - ) - prepareScenario( - stringProviderResult = "Title", - alarmUiModel = alarmUiModel, + fun `should create task details list when create is called from taskDetailsUiModelFactory`() = runBlocking { + val task = + Task( + id = 45L, + name = "Task Name", + notes = "Task Notes", + alarm = + Alarm( + dateTime = Calendar.getInstance(), + repeatType = RepeatType.WEEK, + ), ) + val alarmUiModel = + AlarmUiModel( + date = "07/03/2022", + time = "11:55", + description = "Today, March 7, 2022", + isCustom = false, + shouldRepeat = true, + customDays = null, + ) + prepareScenario( + stringProviderResult = "Title", + alarmUiModel = alarmUiModel, + ) - val result = taskDetailsUiModelFactory.create(task) + val result = taskDetailsUiModelFactory.create(task) - val expected = - listOf( - TitleUiModel("Task Name"), - LabelUiModel("Title"), - alarmUiModel, - LabelValueUiModel( - label = "Title", - value = "Task Notes", - ), - ) - assertEquals(expected, result) - } + val expected = + listOf( + TitleUiModel("Task Name"), + LabelUiModel("Title"), + alarmUiModel, + LabelValueUiModel( + label = "Title", + value = "Task Notes", + ), + ) + assertEquals(expected, result) + } private fun prepareScenario( stringProviderResult: String = "Title", diff --git a/feature/task-details/impl/src/test/java/br/com/sailboat/todozy/feature/task/details/impl/presentation/viewmodel/TaskDetailsViewModelTest.kt b/feature/task-details/impl/src/test/java/br/com/sailboat/todozy/feature/task/details/impl/presentation/viewmodel/TaskDetailsViewModelTest.kt index 096bb2ec..9b07462c 100644 --- a/feature/task-details/impl/src/test/java/br/com/sailboat/todozy/feature/task/details/impl/presentation/viewmodel/TaskDetailsViewModelTest.kt +++ b/feature/task-details/impl/src/test/java/br/com/sailboat/todozy/feature/task/details/impl/presentation/viewmodel/TaskDetailsViewModelTest.kt @@ -65,68 +65,65 @@ internal class TaskDetailsViewModelTest { ) @Test - fun `should call getTaskUseCase when dispatchViewAction is called with OnStart`() = - runTest(coroutinesTestRule.dispatcher) { - val taskId = 42L - val taskDetails = - listOf( - TitleUiModel(title = "Task Name"), - AlarmUiModel( - date = "07/03/2022", - time = "11:55", - description = "Today, March 7, 2022", - isCustom = false, - shouldRepeat = true, - customDays = null, - ), - ) - prepareScenario(taskDetailsResult = taskDetails) + fun `should call getTaskUseCase when dispatchViewAction is called with OnStart`() = runTest(coroutinesTestRule.dispatcher) { + val taskId = 42L + val taskDetails = + listOf( + TitleUiModel(title = "Task Name"), + AlarmUiModel( + date = "07/03/2022", + time = "11:55", + description = "Today, March 7, 2022", + isCustom = false, + shouldRepeat = true, + customDays = null, + ), + ) + prepareScenario(taskDetailsResult = taskDetails) - viewModel.dispatchViewIntent(TaskDetailsViewIntent.OnStart(taskId)) - advanceUntilIdle() + viewModel.dispatchViewIntent(TaskDetailsViewIntent.OnStart(taskId)) + advanceUntilIdle() - coVerify(exactly = 1) { getTaskUseCase(taskId) } - assertEquals(taskDetails, viewModel.viewState.taskDetails.value) - } + coVerify(exactly = 1) { getTaskUseCase(taskId) } + assertEquals(taskDetails, viewModel.viewState.taskDetails.value) + } @Test - fun `should call getTaskMetricsUseCase when dispatchViewAction is called with OnStart`() = - runTest(coroutinesTestRule.dispatcher) { - val taskId = 42L - val taskMetrics = - TaskMetrics( - doneTasks = 15, - notDoneTasks = 2, - consecutiveDone = 5, - ) - prepareScenario(taskMetricsResult = Result.success(taskMetrics)) - - viewModel.dispatchViewIntent(TaskDetailsViewIntent.OnStart(taskId)) - advanceUntilIdle() - - coVerify(exactly = 1) { - getTaskMetricsUseCase( - withArg { filter -> - assertEquals(taskId, filter.taskId) - assertNotNull(filter.initialDate) - assertNotNull(filter.finalDate) - }, - ) - } - assertEquals(taskMetrics, viewModel.viewState.taskMetrics.value) + fun `should call getTaskMetricsUseCase when dispatchViewAction is called with OnStart`() = runTest(coroutinesTestRule.dispatcher) { + val taskId = 42L + val taskMetrics = + TaskMetrics( + doneTasks = 15, + notDoneTasks = 2, + consecutiveDone = 5, + ) + prepareScenario(taskMetricsResult = Result.success(taskMetrics)) + + viewModel.dispatchViewIntent(TaskDetailsViewIntent.OnStart(taskId)) + advanceUntilIdle() + + coVerify(exactly = 1) { + getTaskMetricsUseCase( + withArg { filter -> + assertEquals(taskId, filter.taskId) + assertNotNull(filter.initialDate) + assertNotNull(filter.finalDate) + }, + ) } + assertEquals(taskMetrics, viewModel.viewState.taskMetrics.value) + } @Test - fun `should call getTaskProgressUseCase when dispatchViewAction is called with OnStart`() = - runTest(coroutinesTestRule.dispatcher) { - val taskId = 42L - prepareScenario() + fun `should call getTaskProgressUseCase when dispatchViewAction is called with OnStart`() = runTest(coroutinesTestRule.dispatcher) { + val taskId = 42L + prepareScenario() - viewModel.dispatchViewIntent(TaskDetailsViewIntent.OnStart(taskId)) - advanceUntilIdle() + viewModel.dispatchViewIntent(TaskDetailsViewIntent.OnStart(taskId)) + advanceUntilIdle() - coVerify { getTaskProgressUseCase(TaskProgressFilter(TaskProgressRange.LAST_YEAR, taskId)) } - } + coVerify { getTaskProgressUseCase(TaskProgressFilter(TaskProgressRange.LAST_YEAR, taskId)) } + } @Test fun `should not call getTaskMetricsUseCase when dispatchViewAction is called with OnStart when alarm is null`() = @@ -216,44 +213,42 @@ internal class TaskDetailsViewModelTest { } @Test - fun `should navigate to task form when dispatchViewAction is called with OnClickEditTask`() = - runTest(coroutinesTestRule.dispatcher) { - val taskId = 42L - prepareScenario() + fun `should navigate to task form when dispatchViewAction is called with OnClickEditTask`() = runTest(coroutinesTestRule.dispatcher) { + val taskId = 42L + prepareScenario() - viewModel.dispatchViewIntent(TaskDetailsViewIntent.OnStart(taskId)) - advanceUntilIdle() - viewModel.dispatchViewIntent(TaskDetailsViewIntent.OnClickEditTask) + viewModel.dispatchViewIntent(TaskDetailsViewIntent.OnStart(taskId)) + advanceUntilIdle() + viewModel.dispatchViewIntent(TaskDetailsViewIntent.OnClickEditTask) - val expected = TaskDetailsViewAction.NavigateToTaskForm(taskId) - assertEquals(expected, viewModel.viewState.viewAction.value) - } + val expected = TaskDetailsViewAction.NavigateToTaskForm(taskId) + assertEquals(expected, viewModel.viewState.viewAction.value) + } @Test - fun `should call getTaskUseCase when dispatchViewAction is called with OnReturnToDetails`() = - runTest(coroutinesTestRule.dispatcher) { - val taskId = 42L - val taskDetails = - listOf( - TitleUiModel(title = "Task Name"), - AlarmUiModel( - date = "07/03/2022", - time = "11:55", - description = "Today, March 7, 2022", - isCustom = false, - shouldRepeat = true, - customDays = null, - ), - ) - prepareScenario(taskDetailsResult = taskDetails) - viewModel.viewState.taskId = taskId + fun `should call getTaskUseCase when dispatchViewAction is called with OnReturnToDetails`() = runTest(coroutinesTestRule.dispatcher) { + val taskId = 42L + val taskDetails = + listOf( + TitleUiModel(title = "Task Name"), + AlarmUiModel( + date = "07/03/2022", + time = "11:55", + description = "Today, March 7, 2022", + isCustom = false, + shouldRepeat = true, + customDays = null, + ), + ) + prepareScenario(taskDetailsResult = taskDetails) + viewModel.viewState.taskId = taskId - viewModel.dispatchViewIntent(TaskDetailsViewIntent.OnReturnToDetails) - advanceUntilIdle() + viewModel.dispatchViewIntent(TaskDetailsViewIntent.OnReturnToDetails) + advanceUntilIdle() - coVerify(exactly = 1) { getTaskUseCase(taskId) } - assertEquals(taskDetails, viewModel.viewState.taskDetails.value) - } + coVerify(exactly = 1) { getTaskUseCase(taskId) } + assertEquals(taskDetails, viewModel.viewState.taskDetails.value) + } @Test fun `should call getTaskMetricsUseCase when dispatchViewAction is called with OnReturnToDetails`() = @@ -285,71 +280,69 @@ internal class TaskDetailsViewModelTest { } @Test - fun `should reload progress when dispatchViewAction is called with OnSelectProgressRange`() = - runTest(coroutinesTestRule.dispatcher) { - val taskId = 42L - viewModel.viewState.taskId = taskId - prepareScenario() + fun `should reload progress when dispatchViewAction is called with OnSelectProgressRange`() = runTest(coroutinesTestRule.dispatcher) { + val taskId = 42L + viewModel.viewState.taskId = taskId + prepareScenario() - viewModel.dispatchViewIntent( - TaskDetailsViewIntent.OnSelectProgressRange(TaskProgressRange.LAST_30_DAYS), - ) - advanceUntilIdle() + viewModel.dispatchViewIntent( + TaskDetailsViewIntent.OnSelectProgressRange(TaskProgressRange.LAST_30_DAYS), + ) + advanceUntilIdle() - coVerify { - getTaskProgressUseCase(TaskProgressFilter(TaskProgressRange.LAST_30_DAYS, taskId)) - } - assertEquals(TaskProgressRange.LAST_30_DAYS, viewModel.viewState.taskProgressRange.value) + coVerify { + getTaskProgressUseCase(TaskProgressFilter(TaskProgressRange.LAST_30_DAYS, taskId)) } + assertEquals(TaskProgressRange.LAST_30_DAYS, viewModel.viewState.taskProgressRange.value) + } @Test - fun `should show progress only for scheduled repeat days`() = - runTest(coroutinesTestRule.dispatcher) { - val taskId = 7L - val startDate = LocalDate.of(2024, 8, 12) // Monday - val progressDays = - (0..6).map { offset -> - val date = startDate.plusDays(offset.toLong()) - TaskProgressDay( - date = date, - doneCount = if (date.dayOfWeek == DayOfWeek.MONDAY) 1 else 0, - notDoneCount = if (date.dayOfWeek == DayOfWeek.TUESDAY) 1 else 0, - totalCount = - when (date.dayOfWeek) { - DayOfWeek.MONDAY, DayOfWeek.TUESDAY -> 1 - else -> 0 - }, - ) - } - val alarmCalendar = - Calendar.getInstance().apply { - set(2024, Calendar.AUGUST, 12, 8, 0, 0) - } - val repeatingAlarm = - Alarm( - dateTime = alarmCalendar, - repeatType = RepeatType.CUSTOM, - customDays = "${Calendar.MONDAY}${Calendar.WEDNESDAY}${Calendar.FRIDAY}", + fun `should show progress only for scheduled repeat days`() = runTest(coroutinesTestRule.dispatcher) { + val taskId = 7L + val startDate = LocalDate.of(2024, 8, 12) // Monday + val progressDays = + (0..6).map { offset -> + val date = startDate.plusDays(offset.toLong()) + TaskProgressDay( + date = date, + doneCount = if (date.dayOfWeek == DayOfWeek.MONDAY) 1 else 0, + notDoneCount = if (date.dayOfWeek == DayOfWeek.TUESDAY) 1 else 0, + totalCount = + when (date.dayOfWeek) { + DayOfWeek.MONDAY, DayOfWeek.TUESDAY -> 1 + else -> 0 + }, ) - - prepareScenario( - taskResult = Result.success(TaskMockFactory.makeTask(id = taskId, alarm = repeatingAlarm)), - taskProgressResult = Result.success(progressDays), + } + val alarmCalendar = + Calendar.getInstance().apply { + set(2024, Calendar.AUGUST, 12, 8, 0, 0) + } + val repeatingAlarm = + Alarm( + dateTime = alarmCalendar, + repeatType = RepeatType.CUSTOM, + customDays = "${Calendar.MONDAY}${Calendar.WEDNESDAY}${Calendar.FRIDAY}", ) - viewModel.dispatchViewIntent(TaskDetailsViewIntent.OnStart(taskId)) - advanceUntilIdle() + prepareScenario( + taskResult = Result.success(TaskMockFactory.makeTask(id = taskId, alarm = repeatingAlarm)), + taskProgressResult = Result.success(progressDays), + ) - val expectedDayOrder = - listOf( - DayOfWeek.MONDAY, - DayOfWeek.WEDNESDAY, - DayOfWeek.FRIDAY, - ) - assertEquals(expectedDayOrder, viewModel.viewState.taskProgressDayOrder.value) - val expectedProgress = progressDays.filter { it.date.dayOfWeek in expectedDayOrder } - assertEquals(expectedProgress, viewModel.viewState.taskProgressDays.value) - } + viewModel.dispatchViewIntent(TaskDetailsViewIntent.OnStart(taskId)) + advanceUntilIdle() + + val expectedDayOrder = + listOf( + DayOfWeek.MONDAY, + DayOfWeek.WEDNESDAY, + DayOfWeek.FRIDAY, + ) + assertEquals(expectedDayOrder, viewModel.viewState.taskProgressDayOrder.value) + val expectedProgress = progressDays.filter { it.date.dayOfWeek in expectedDayOrder } + assertEquals(expectedProgress, viewModel.viewState.taskProgressDays.value) + } @Test fun `should not call getTaskMetricsUseCase when dispatchViewAction is called with OnReturnToDetails when alarm is null`() = diff --git a/feature/task-form/impl/src/main/java/br/com/sailboat/todozy/feature/task/form/impl/domain/usecase/SaveTaskUseCaseImpl.kt b/feature/task-form/impl/src/main/java/br/com/sailboat/todozy/feature/task/form/impl/domain/usecase/SaveTaskUseCaseImpl.kt index e61196f5..5bef2217 100644 --- a/feature/task-form/impl/src/main/java/br/com/sailboat/todozy/feature/task/form/impl/domain/usecase/SaveTaskUseCaseImpl.kt +++ b/feature/task-form/impl/src/main/java/br/com/sailboat/todozy/feature/task/form/impl/domain/usecase/SaveTaskUseCaseImpl.kt @@ -14,24 +14,23 @@ internal class SaveTaskUseCaseImpl( private val saveAlarmUseCase: SaveAlarmUseCase, private val checkTaskFieldsUseCase: CheckTaskFieldsUseCase, ) : SaveTaskUseCase { - override suspend operator fun invoke(task: Task): Result = - runCatching { - val conditions = checkTaskFieldsUseCase(task) + override suspend operator fun invoke(task: Task): Result = runCatching { + val conditions = checkTaskFieldsUseCase(task) - if (conditions.isNotEmpty()) { - throw TaskFieldsException(conditions) - } + if (conditions.isNotEmpty()) { + throw TaskFieldsException(conditions) + } - val result = - if (task.id == Entity.NO_ID) { - taskRepository.insert(task).getOrThrow() - } else { - deleteAlarmUseCase(task.id) - taskRepository.update(task).getOrThrow() - } + val result = + if (task.id == Entity.NO_ID) { + taskRepository.insert(task).getOrThrow() + } else { + deleteAlarmUseCase(task.id) + taskRepository.update(task).getOrThrow() + } - task.alarm?.run { saveAlarmUseCase(this, result.id) } + task.alarm?.run { saveAlarmUseCase(this, result.id) } - return@runCatching result - } + return@runCatching result + } } diff --git a/feature/task-form/impl/src/main/java/br/com/sailboat/todozy/feature/task/form/impl/presentation/TaskFormFragment.kt b/feature/task-form/impl/src/main/java/br/com/sailboat/todozy/feature/task/form/impl/presentation/TaskFormFragment.kt index 60928b10..d31e828f 100644 --- a/feature/task-form/impl/src/main/java/br/com/sailboat/todozy/feature/task/form/impl/presentation/TaskFormFragment.kt +++ b/feature/task-form/impl/src/main/java/br/com/sailboat/todozy/feature/task/form/impl/presentation/TaskFormFragment.kt @@ -62,13 +62,12 @@ internal class TaskFormFragment : Fragment() { fun newInstance() = TaskFormFragment() - fun newInstance(taskId: Long): TaskFormFragment = - with(TaskFormFragment()) { - val bundle = Bundle() - bundle.putTaskId(taskId) - arguments = bundle - return this - } + fun newInstance(taskId: Long): TaskFormFragment = with(TaskFormFragment()) { + val bundle = Bundle() + bundle.putTaskId(taskId) + arguments = bundle + return this + } } override fun onCreateView( @@ -155,13 +154,12 @@ internal class TaskFormFragment : Fragment() { activity?.showKeyboard(binding.etTaskFormName) } - private fun setTaskDetails(action: TaskFormViewAction.SetTaskDetails) = - with(binding) { - etTaskFormNotes.setText(action.taskNotes) + private fun setTaskDetails(action: TaskFormViewAction.SetTaskDetails) = with(binding) { + etTaskFormNotes.setText(action.taskNotes) - etTaskFormName.setText(action.taskName) - etTaskFormName.setSelection(etTaskFormName.length()) - } + etTaskFormName.setText(action.taskName) + etTaskFormName.setSelection(etTaskFormName.length()) + } private fun showErrorSavingTask() { Toast.makeText(activity, UiR.string.msg_error, Toast.LENGTH_SHORT).show() @@ -339,21 +337,20 @@ internal class TaskFormFragment : Fragment() { Toast.makeText(activity, getString(UiR.string.alarm_not_valid), Toast.LENGTH_SHORT).show() } - private fun initAlarmViews() = - with(binding) { - rlTaskFormAddAlarm.setSafeClickListener { - viewModel.dispatchViewIntent(OnClickAddAlarm) - } - alarmDetails.tvAlarmDate.setSafeClickListener { - viewModel.dispatchViewIntent(OnClickAlarmDate) - } - alarmDetails.tvAlarmTime.setSafeClickListener { - viewModel.dispatchViewIntent(OnClickAlarmTime) - } - alarmDetails.tvAlarmRepeat.setSafeClickListener { - viewModel.dispatchViewIntent(OnClickRepeatAlarm) - } + private fun initAlarmViews() = with(binding) { + rlTaskFormAddAlarm.setSafeClickListener { + viewModel.dispatchViewIntent(OnClickAddAlarm) + } + alarmDetails.tvAlarmDate.setSafeClickListener { + viewModel.dispatchViewIntent(OnClickAlarmDate) } + alarmDetails.tvAlarmTime.setSafeClickListener { + viewModel.dispatchViewIntent(OnClickAlarmTime) + } + alarmDetails.tvAlarmRepeat.setSafeClickListener { + viewModel.dispatchViewIntent(OnClickRepeatAlarm) + } + } private fun initEditTexts() { binding.etTaskFormName.setOnKeyListener( diff --git a/feature/task-form/impl/src/main/java/br/com/sailboat/todozy/feature/task/form/impl/presentation/viewmodel/TaskFormViewModel.kt b/feature/task-form/impl/src/main/java/br/com/sailboat/todozy/feature/task/form/impl/presentation/viewmodel/TaskFormViewModel.kt index 4cb7ea3f..df284636 100644 --- a/feature/task-form/impl/src/main/java/br/com/sailboat/todozy/feature/task/form/impl/presentation/viewmodel/TaskFormViewModel.kt +++ b/feature/task-form/impl/src/main/java/br/com/sailboat/todozy/feature/task/form/impl/presentation/viewmodel/TaskFormViewModel.kt @@ -156,28 +156,27 @@ internal class TaskFormViewModel( viewState.viewAction.value = TaskFormViewAction.NavigateToCustomRepeatAlarmSelector(viewState.selectedDays) } - private fun startEditingTask() = - viewModelScope.launch { - try { - val task = getTaskUseCase(viewState.taskId).getOrThrow() - - viewState.alarm = task.alarm?.dateTime - viewState.repeatAlarmType = task.alarm?.repeatType ?: RepeatType.NOT_REPEAT - viewState.selectedDays = task.alarm?.customDays - - viewState.viewAction.value = - TaskFormViewAction.SetTaskDetails( - taskName = task.name, - taskNotes = task.notes, - ) - - updateAlarm() - } catch (e: Exception) { - logService.error(e) - viewState.viewAction.value = TaskFormViewAction.ShowErrorSavingTask - viewState.viewAction.value = TaskFormViewAction.CloseTaskForm(success = false) - } + private fun startEditingTask() = viewModelScope.launch { + try { + val task = getTaskUseCase(viewState.taskId).getOrThrow() + + viewState.alarm = task.alarm?.dateTime + viewState.repeatAlarmType = task.alarm?.repeatType ?: RepeatType.NOT_REPEAT + viewState.selectedDays = task.alarm?.customDays + + viewState.viewAction.value = + TaskFormViewAction.SetTaskDetails( + taskName = task.name, + taskNotes = task.notes, + ) + + updateAlarm() + } catch (e: Exception) { + logService.error(e) + viewState.viewAction.value = TaskFormViewAction.ShowErrorSavingTask + viewState.viewAction.value = TaskFormViewAction.CloseTaskForm(success = false) } + } private fun updateAlarm(animate: Boolean = false) { val repeatType = diff --git a/feature/task-form/impl/src/test/java/br/com/sailboat/todozy/feature/task/form/impl/domain/usecase/CheckTaskFieldsUseCaseImplTest.kt b/feature/task-form/impl/src/test/java/br/com/sailboat/todozy/feature/task/form/impl/domain/usecase/CheckTaskFieldsUseCaseImplTest.kt index f39be146..f65a83e9 100644 --- a/feature/task-form/impl/src/test/java/br/com/sailboat/todozy/feature/task/form/impl/domain/usecase/CheckTaskFieldsUseCaseImplTest.kt +++ b/feature/task-form/impl/src/test/java/br/com/sailboat/todozy/feature/task/form/impl/domain/usecase/CheckTaskFieldsUseCaseImplTest.kt @@ -14,59 +14,56 @@ internal class CheckTaskFieldsUseCaseImplTest { private val checkTaskFieldsUseCase = CheckTaskFieldsUseCaseImpl() @Test - fun `should get TASK_NAME_NOT_FILLED when task name is empty`() = - runBlocking { - val task = Task(id = Entity.NO_ID, name = "", notes = "Some notes") + fun `should get TASK_NAME_NOT_FILLED when task name is empty`() = runBlocking { + val task = Task(id = Entity.NO_ID, name = "", notes = "Some notes") - val result = checkTaskFieldsUseCase(task) + val result = checkTaskFieldsUseCase(task) - val expected = listOf(TaskFieldsConditions.TASK_NAME_NOT_FILLED) - assertEquals(expected, result) - } + val expected = listOf(TaskFieldsConditions.TASK_NAME_NOT_FILLED) + assertEquals(expected, result) + } @Test - fun `should get ALARM_NOT_VALID when task alarm is before now`() = - runBlocking { - val task = - Task( - id = Entity.NO_ID, - name = "Task", - notes = "Some notes", - alarm = - Alarm( - dateTime = Calendar.getInstance().apply { add(Calendar.HOUR_OF_DAY, -1) }, - repeatType = RepeatType.NOT_REPEAT, - ), - ) + fun `should get ALARM_NOT_VALID when task alarm is before now`() = runBlocking { + val task = + Task( + id = Entity.NO_ID, + name = "Task", + notes = "Some notes", + alarm = + Alarm( + dateTime = Calendar.getInstance().apply { add(Calendar.HOUR_OF_DAY, -1) }, + repeatType = RepeatType.NOT_REPEAT, + ), + ) - val result = checkTaskFieldsUseCase(task) + val result = checkTaskFieldsUseCase(task) - val expected = listOf(TaskFieldsConditions.ALARM_NOT_VALID) - assertEquals(expected, result) - } + val expected = listOf(TaskFieldsConditions.ALARM_NOT_VALID) + assertEquals(expected, result) + } @Test - fun `should get TASK_NAME_NOT_FILLED and ALARM_NOT_VALID when task alarm is before now`() = - runBlocking { - val task = - Task( - id = Entity.NO_ID, - name = "", - notes = "Some notes", - alarm = - Alarm( - dateTime = Calendar.getInstance().apply { add(Calendar.HOUR_OF_DAY, -1) }, - repeatType = RepeatType.NOT_REPEAT, - ), - ) + fun `should get TASK_NAME_NOT_FILLED and ALARM_NOT_VALID when task alarm is before now`() = runBlocking { + val task = + Task( + id = Entity.NO_ID, + name = "", + notes = "Some notes", + alarm = + Alarm( + dateTime = Calendar.getInstance().apply { add(Calendar.HOUR_OF_DAY, -1) }, + repeatType = RepeatType.NOT_REPEAT, + ), + ) - val result = checkTaskFieldsUseCase(task) + val result = checkTaskFieldsUseCase(task) - val expected = - listOf( - TaskFieldsConditions.TASK_NAME_NOT_FILLED, - TaskFieldsConditions.ALARM_NOT_VALID, - ) - assertEquals(expected, result) - } + val expected = + listOf( + TaskFieldsConditions.TASK_NAME_NOT_FILLED, + TaskFieldsConditions.ALARM_NOT_VALID, + ) + assertEquals(expected, result) + } } diff --git a/feature/task-form/impl/src/test/java/br/com/sailboat/todozy/feature/task/form/impl/domain/usecase/SaveTaskUseCaseImplTest.kt b/feature/task-form/impl/src/test/java/br/com/sailboat/todozy/feature/task/form/impl/domain/usecase/SaveTaskUseCaseImplTest.kt index 19ad99eb..1067fcef 100644 --- a/feature/task-form/impl/src/test/java/br/com/sailboat/todozy/feature/task/form/impl/domain/usecase/SaveTaskUseCaseImplTest.kt +++ b/feature/task-form/impl/src/test/java/br/com/sailboat/todozy/feature/task/form/impl/domain/usecase/SaveTaskUseCaseImplTest.kt @@ -31,98 +31,92 @@ internal class SaveTaskUseCaseImplTest { ) @Test - fun `should insert task in the repository when task has no id`() = - runBlocking { - val task = Task(id = Entity.NO_ID, name = "Task Name", notes = "Some notes") - prepareScenario() + fun `should insert task in the repository when task has no id`() = runBlocking { + val task = Task(id = Entity.NO_ID, name = "Task Name", notes = "Some notes") + prepareScenario() - saveTaskUseCase(task) + saveTaskUseCase(task) - coVerify(exactly = 1) { repository.insert(task) } - coVerify(exactly = 0) { repository.update(task) } - confirmVerified(repository) - } + coVerify(exactly = 1) { repository.insert(task) } + coVerify(exactly = 0) { repository.update(task) } + confirmVerified(repository) + } @Test - fun `should update task in the repository when task has some id`() = - runBlocking { - prepareScenario() - val task = Task(id = 45, name = "Task Name", notes = "Some notes") + fun `should update task in the repository when task has some id`() = runBlocking { + prepareScenario() + val task = Task(id = 45, name = "Task Name", notes = "Some notes") - saveTaskUseCase(task) + saveTaskUseCase(task) - coVerify(exactly = 0) { repository.insert(task) } - coVerify(exactly = 1) { repository.update(task) } - confirmVerified(repository) - } + coVerify(exactly = 0) { repository.insert(task) } + coVerify(exactly = 1) { repository.update(task) } + confirmVerified(repository) + } @Test - fun `should check task fields when save task is called`() = - runBlocking { - prepareScenario() - val task = Task(id = 45, name = "Task Name", notes = "Some notes") + fun `should check task fields when save task is called`() = runBlocking { + prepareScenario() + val task = Task(id = 45, name = "Task Name", notes = "Some notes") - saveTaskUseCase(task) + saveTaskUseCase(task) - coVerify(exactly = 1) { checkTaskFieldsUseCase(task) } - } + coVerify(exactly = 1) { checkTaskFieldsUseCase(task) } + } @Test - fun `should delete alarm when update task`() = - runBlocking { - prepareScenario() - val alarm = - Alarm( - dateTime = Calendar.getInstance().apply { add(Calendar.DATE, 1) }, - repeatType = RepeatType.NOT_REPEAT, - ) - val task = Task(id = 45, name = "Task Name", notes = "Some notes", alarm = alarm) - - saveTaskUseCase(task) - - coVerify { deleteAlarmUseCase(45) } - confirmVerified(deleteAlarmUseCase) - } + fun `should delete alarm when update task`() = runBlocking { + prepareScenario() + val alarm = + Alarm( + dateTime = Calendar.getInstance().apply { add(Calendar.DATE, 1) }, + repeatType = RepeatType.NOT_REPEAT, + ) + val task = Task(id = 45, name = "Task Name", notes = "Some notes", alarm = alarm) + + saveTaskUseCase(task) + + coVerify { deleteAlarmUseCase(45) } + confirmVerified(deleteAlarmUseCase) + } @Test - fun `should save alarm when inserting task`() = - runBlocking { - val alarm = - Alarm( - dateTime = Calendar.getInstance().apply { add(Calendar.DATE, 1) }, - repeatType = RepeatType.NOT_REPEAT, - ) - val task = - Task( - id = Entity.NO_ID, - name = "Task Name", - notes = "Some notes", - alarm = alarm, - ) - prepareScenario(insertTaskResult = Result.success(task)) - - saveTaskUseCase(task) - - coVerify { saveAlarmUseCase(alarm, task.id) } - confirmVerified(saveAlarmUseCase) - } + fun `should save alarm when inserting task`() = runBlocking { + val alarm = + Alarm( + dateTime = Calendar.getInstance().apply { add(Calendar.DATE, 1) }, + repeatType = RepeatType.NOT_REPEAT, + ) + val task = + Task( + id = Entity.NO_ID, + name = "Task Name", + notes = "Some notes", + alarm = alarm, + ) + prepareScenario(insertTaskResult = Result.success(task)) + + saveTaskUseCase(task) + + coVerify { saveAlarmUseCase(alarm, task.id) } + confirmVerified(saveAlarmUseCase) + } @Test - fun `should save alarm when updating task`() = - runBlocking { - prepareScenario() - val alarm = - Alarm( - dateTime = Calendar.getInstance().apply { add(Calendar.DATE, 1) }, - repeatType = RepeatType.NOT_REPEAT, - ) - val task = Task(id = 45, name = "Task Name", notes = "Some notes", alarm = alarm) - - saveTaskUseCase(task) - - coVerify { saveAlarmUseCase(alarm, task.id) } - confirmVerified(saveAlarmUseCase) - } + fun `should save alarm when updating task`() = runBlocking { + prepareScenario() + val alarm = + Alarm( + dateTime = Calendar.getInstance().apply { add(Calendar.DATE, 1) }, + repeatType = RepeatType.NOT_REPEAT, + ) + val task = Task(id = 45, name = "Task Name", notes = "Some notes", alarm = alarm) + + saveTaskUseCase(task) + + coVerify { saveAlarmUseCase(alarm, task.id) } + confirmVerified(saveAlarmUseCase) + } private fun prepareScenario( insertTaskResult: Result = Result.success(TaskMockFactory.makeTask()), diff --git a/feature/task-history/impl/src/main/java/br/com/sailboat/todozy/feature/task/history/impl/data/datasource/TaskHistoryLocalDataSourceSQLite.kt b/feature/task-history/impl/src/main/java/br/com/sailboat/todozy/feature/task/history/impl/data/datasource/TaskHistoryLocalDataSourceSQLite.kt index 9341cb82..11034c71 100644 --- a/feature/task-history/impl/src/main/java/br/com/sailboat/todozy/feature/task/history/impl/data/datasource/TaskHistoryLocalDataSourceSQLite.kt +++ b/feature/task-history/impl/src/main/java/br/com/sailboat/todozy/feature/task/history/impl/data/datasource/TaskHistoryLocalDataSourceSQLite.kt @@ -17,126 +17,117 @@ internal class TaskHistoryLocalDataSourceSQLite( override fun save( taskId: Long, taskStatus: Int, - ): Result = - runCatching { - val sql = StringBuilder() + ): Result = runCatching { + val sql = StringBuilder() - sql.append(" INSERT INTO TaskHistory ") - sql.append(" (fkTaskId, status, insertingDate, enabled) ") - sql.append(" VALUES (?, ?, ?, ?); ") + sql.append(" INSERT INTO TaskHistory ") + sql.append(" (fkTaskId, status, insertingDate, enabled) ") + sql.append(" VALUES (?, ?, ?, ?); ") - val statement = compileStatement(sql.toString()) - statement.bindLong(1, taskId) - statement.bindLong(2, taskStatus.toLong()) - statement.bindString(3, parseCalendarToString(Calendar.getInstance())) - statement.bindLong(4, parseBooleanToInt(true).toLong()) + val statement = compileStatement(sql.toString()) + statement.bindLong(1, taskId) + statement.bindLong(2, taskStatus.toLong()) + statement.bindString(3, parseCalendarToString(Calendar.getInstance())) + statement.bindLong(4, parseBooleanToInt(true).toLong()) - return@runCatching insert(statement) - } + return@runCatching insert(statement) + } - override fun update(taskHistoryData: TaskHistoryData) = - runCatching { - val sql = StringBuilder() - sql.append(" UPDATE TaskHistory SET ") - sql.append(" status = ?, ") - sql.append(" enabled = ? ") - sql.append(" WHERE id = ? ") + override fun update(taskHistoryData: TaskHistoryData) = runCatching { + val sql = StringBuilder() + sql.append(" UPDATE TaskHistory SET ") + sql.append(" status = ?, ") + sql.append(" enabled = ? ") + sql.append(" WHERE id = ? ") - val statement = compileStatement(sql.toString()) - statement.bindLong(1, taskHistoryData.status.toLong()) - statement.bindLong(2, parseBooleanToInt(taskHistoryData.enabled).toLong()) - statement.bindLong(3, taskHistoryData.id) + val statement = compileStatement(sql.toString()) + statement.bindLong(1, taskHistoryData.status.toLong()) + statement.bindLong(2, parseBooleanToInt(taskHistoryData.enabled).toLong()) + statement.bindLong(3, taskHistoryData.id) - update(statement) - } + update(statement) + } - override fun delete(taskHistoryId: Long) = - runCatching { - val sql = StringBuilder() - sql.append(" DELETE FROM TaskHistory WHERE TaskHistory.id = ?") + override fun delete(taskHistoryId: Long) = runCatching { + val sql = StringBuilder() + sql.append(" DELETE FROM TaskHistory WHERE TaskHistory.id = ?") - val statement = compileStatement(sql.toString()) - statement.bindLong(1, taskHistoryId) + val statement = compileStatement(sql.toString()) + statement.bindLong(1, taskHistoryId) - delete(statement) - } + delete(statement) + } - override fun deleteAllHistory() = - runCatching { - val delete = " DELETE FROM TaskHistory" - delete(compileStatement(delete)) - } + override fun deleteAllHistory() = runCatching { + val delete = " DELETE FROM TaskHistory" + delete(compileStatement(delete)) + } - override fun getHistory(filter: TaskHistoryFilter) = - runCatching { - val query = TaskHistoryQueryBuilder() - query.bindDefaultSelect() - query.bindDefaultInnerJoin() - query.bindDefaultWhere() - query.bindWhereEnabled() - query.bindWhereFilter(filter) - query.bindWhereDateRange(filter) - query.bindDefaultOrderBy() - - return@runCatching getTaskHistoryList(query.toString(), filter) - } + override fun getHistory(filter: TaskHistoryFilter) = runCatching { + val query = TaskHistoryQueryBuilder() + query.bindDefaultSelect() + query.bindDefaultInnerJoin() + query.bindDefaultWhere() + query.bindWhereEnabled() + query.bindWhereFilter(filter) + query.bindWhereDateRange(filter) + query.bindDefaultOrderBy() - override fun getTodayHistory(filter: TaskHistoryFilter) = - runCatching { - val query = TaskHistoryQueryBuilder() - query.bindDefaultSelect() - query.bindDefaultInnerJoin() - query.bindDefaultWhere() - query.bindWhereToday() - query.bindWhereEnabled() - query.bindWhereFilter(filter) - query.bindWhereDateRange(filter) - query.bindDefaultOrderBy() - - return@runCatching getTaskHistoryList(query.toString(), filter) - } + return@runCatching getTaskHistoryList(query.toString(), filter) + } - override fun getYesterdayHistory(filter: TaskHistoryFilter) = - runCatching { - val query = TaskHistoryQueryBuilder() - query.bindDefaultSelect() - query.bindDefaultInnerJoin() - query.bindDefaultWhere() - query.bindWhereYesterday() - query.bindWhereEnabled() - query.bindWhereFilter(filter) - query.bindWhereDateRange(filter) - query.bindDefaultOrderBy() - - return@runCatching getTaskHistoryList(query.toString(), filter) - } + override fun getTodayHistory(filter: TaskHistoryFilter) = runCatching { + val query = TaskHistoryQueryBuilder() + query.bindDefaultSelect() + query.bindDefaultInnerJoin() + query.bindDefaultWhere() + query.bindWhereToday() + query.bindWhereEnabled() + query.bindWhereFilter(filter) + query.bindWhereDateRange(filter) + query.bindDefaultOrderBy() - override fun getPreviousDaysHistory(filter: TaskHistoryFilter) = - runCatching { - val query = TaskHistoryQueryBuilder() - query.bindDefaultSelect() - query.bindDefaultInnerJoin() - query.bindDefaultWhere() - query.bindWherePreviousDays() - query.bindWhereEnabled() - query.bindWhereFilter(filter) - query.bindWhereDateRange(filter) - query.bindDefaultOrderBy() - - return@runCatching getTaskHistoryList(query.toString(), filter) - } + return@runCatching getTaskHistoryList(query.toString(), filter) + } - override fun getTaskHistoryByTask(taskId: Long) = - runCatching { - val query = TaskHistoryQueryBuilder() - query.bindDefaultSelect() - query.bindDefaultInnerJoin() - query.bindDefaultWhere() - query.bindWhereTaskId(taskId) - query.bindDefaultOrderBy() + override fun getYesterdayHistory(filter: TaskHistoryFilter) = runCatching { + val query = TaskHistoryQueryBuilder() + query.bindDefaultSelect() + query.bindDefaultInnerJoin() + query.bindDefaultWhere() + query.bindWhereYesterday() + query.bindWhereEnabled() + query.bindWhereFilter(filter) + query.bindWhereDateRange(filter) + query.bindDefaultOrderBy() - return@runCatching getTaskHistoryList(query.toString(), null) - } + return@runCatching getTaskHistoryList(query.toString(), filter) + } + + override fun getPreviousDaysHistory(filter: TaskHistoryFilter) = runCatching { + val query = TaskHistoryQueryBuilder() + query.bindDefaultSelect() + query.bindDefaultInnerJoin() + query.bindDefaultWhere() + query.bindWherePreviousDays() + query.bindWhereEnabled() + query.bindWhereFilter(filter) + query.bindWhereDateRange(filter) + query.bindDefaultOrderBy() + + return@runCatching getTaskHistoryList(query.toString(), filter) + } + + override fun getTaskHistoryByTask(taskId: Long) = runCatching { + val query = TaskHistoryQueryBuilder() + query.bindDefaultSelect() + query.bindDefaultInnerJoin() + query.bindDefaultWhere() + query.bindWhereTaskId(taskId) + query.bindDefaultOrderBy() + + return@runCatching getTaskHistoryList(query.toString(), null) + } fun getPreviousDaysHistoryFromDate( initialDate: Calendar, @@ -155,43 +146,40 @@ internal class TaskHistoryLocalDataSourceSQLite( return getTaskHistoryList(query.toString(), filter) } - override fun getTotalOfDoneTasks(filter: TaskHistoryFilter): Result = - runCatching { - val query = TaskHistoryQueryBuilder() - query.bindSelectCount() - query.bindDefaultInnerJoin() - query.bindDefaultWhere() - query.bindWhereDateRange(filter) - query.bindWhereTaskDone() - query.bindWhereFilter(filter) + override fun getTotalOfDoneTasks(filter: TaskHistoryFilter): Result = runCatching { + val query = TaskHistoryQueryBuilder() + query.bindSelectCount() + query.bindDefaultInnerJoin() + query.bindDefaultWhere() + query.bindWhereDateRange(filter) + query.bindWhereTaskDone() + query.bindWhereFilter(filter) - return@runCatching getCountFromQuery(query.toString(), filter) - } + return@runCatching getCountFromQuery(query.toString(), filter) + } - override fun getTotalOfNotDoneTasks(filter: TaskHistoryFilter): Result = - runCatching { - val query = TaskHistoryQueryBuilder() - query.bindSelectCount() - query.bindDefaultInnerJoin() - query.bindDefaultWhere() - query.bindWhereDateRange(filter) - query.bindWhereTaskNotDone() - query.bindWhereFilter(filter) - - return@runCatching getCountFromQuery(query.toString(), filter) - } + override fun getTotalOfNotDoneTasks(filter: TaskHistoryFilter): Result = runCatching { + val query = TaskHistoryQueryBuilder() + query.bindSelectCount() + query.bindDefaultInnerJoin() + query.bindDefaultWhere() + query.bindWhereDateRange(filter) + query.bindWhereTaskNotDone() + query.bindWhereFilter(filter) - fun getTotalOfDoneTasks(taskId: Long): Result = - runCatching { - val query = TaskHistoryQueryBuilder() - query.bindSelectCount() - query.bindDefaultInnerJoin() - query.bindDefaultWhere() - query.bindWhereTaskId(taskId) - query.bindWhereTaskDone() + return@runCatching getCountFromQuery(query.toString(), filter) + } - return@runCatching getCountFromQuery(query.toString()) - } + fun getTotalOfDoneTasks(taskId: Long): Result = runCatching { + val query = TaskHistoryQueryBuilder() + query.bindSelectCount() + query.bindDefaultInnerJoin() + query.bindDefaultWhere() + query.bindWhereTaskId(taskId) + query.bindWhereTaskDone() + + return@runCatching getCountFromQuery(query.toString()) + } fun getTotalOfNotDoneTasks(taskId: Long): Int { val query = TaskHistoryQueryBuilder() @@ -240,13 +228,12 @@ internal class TaskHistoryLocalDataSourceSQLite( return historyList } - private fun Cursor.mapToTaskHistoryData() = - TaskHistoryData( - id = getLong(this, "id") ?: Entity.NO_ID, - taskId = getLong(this, "fkTaskId") ?: Entity.NO_ID, - taskName = getString(this, "name"), - status = getInt(this, "status") ?: TaskStatus.DONE.id, - insertingDate = getString(this, "insertingDate"), - enabled = getBoolean(this, "enabled") ?: true, - ) + private fun Cursor.mapToTaskHistoryData() = TaskHistoryData( + id = getLong(this, "id") ?: Entity.NO_ID, + taskId = getLong(this, "fkTaskId") ?: Entity.NO_ID, + taskName = getString(this, "name"), + status = getInt(this, "status") ?: TaskStatus.DONE.id, + insertingDate = getString(this, "insertingDate"), + enabled = getBoolean(this, "enabled") ?: true, + ) } diff --git a/feature/task-history/impl/src/main/java/br/com/sailboat/todozy/feature/task/history/impl/data/model/TaskHistoryData.kt b/feature/task-history/impl/src/main/java/br/com/sailboat/todozy/feature/task/history/impl/data/model/TaskHistoryData.kt index de07d044..36c916f2 100644 --- a/feature/task-history/impl/src/main/java/br/com/sailboat/todozy/feature/task/history/impl/data/model/TaskHistoryData.kt +++ b/feature/task-history/impl/src/main/java/br/com/sailboat/todozy/feature/task/history/impl/data/model/TaskHistoryData.kt @@ -14,23 +14,21 @@ internal data class TaskHistoryData( ) // TODO: Move mappers into a specific class -internal fun TaskHistoryData.mapToTaskHistory() = - TaskHistory( - id = id, - taskId = taskId, - taskName = taskName.orEmpty(), - status = TaskStatus.getById(status), - insertingDate = insertingDate.orEmpty(), - ) +internal fun TaskHistoryData.mapToTaskHistory() = TaskHistory( + id = id, + taskId = taskId, + taskName = taskName.orEmpty(), + status = TaskStatus.getById(status), + insertingDate = insertingDate.orEmpty(), +) internal fun List.mapToTaskHistoryList() = map { it.mapToTaskHistory() } -internal fun TaskHistory.mapToTaskHistoryData(): TaskHistoryData = - TaskHistoryData( - id = id, - taskId = taskId, - taskName = taskName, - status = status.id, - insertingDate = insertingDate, - enabled = true, - ) +internal fun TaskHistory.mapToTaskHistoryData(): TaskHistoryData = TaskHistoryData( + id = id, + taskId = taskId, + taskName = taskName, + status = status.id, + insertingDate = insertingDate, + enabled = true, +) diff --git a/feature/task-history/impl/src/main/java/br/com/sailboat/todozy/feature/task/history/impl/data/repository/TaskHistoryRepositoryImpl.kt b/feature/task-history/impl/src/main/java/br/com/sailboat/todozy/feature/task/history/impl/data/repository/TaskHistoryRepositoryImpl.kt index 85bcc6f9..88db85a3 100644 --- a/feature/task-history/impl/src/main/java/br/com/sailboat/todozy/feature/task/history/impl/data/repository/TaskHistoryRepositoryImpl.kt +++ b/feature/task-history/impl/src/main/java/br/com/sailboat/todozy/feature/task/history/impl/data/repository/TaskHistoryRepositoryImpl.kt @@ -20,35 +20,30 @@ internal class TaskHistoryRepositoryImpl( return taskHistoryLocalDataSource.getTotalOfDoneTasks(filter) } - override suspend fun getTodayHistory(filter: TaskHistoryFilter) = - runCatching { - val taskHistoryList = taskHistoryLocalDataSource.getTodayHistory(filter).getOrThrow() - return@runCatching taskHistoryList.mapToTaskHistoryList() - } + override suspend fun getTodayHistory(filter: TaskHistoryFilter) = runCatching { + val taskHistoryList = taskHistoryLocalDataSource.getTodayHistory(filter).getOrThrow() + return@runCatching taskHistoryList.mapToTaskHistoryList() + } - override suspend fun getYesterdayHistory(filter: TaskHistoryFilter) = - runCatching { - val taskHistoryList = taskHistoryLocalDataSource.getYesterdayHistory(filter).getOrThrow() - return@runCatching taskHistoryList.mapToTaskHistoryList() - } + override suspend fun getYesterdayHistory(filter: TaskHistoryFilter) = runCatching { + val taskHistoryList = taskHistoryLocalDataSource.getYesterdayHistory(filter).getOrThrow() + return@runCatching taskHistoryList.mapToTaskHistoryList() + } - override suspend fun getPreviousDaysHistory(filter: TaskHistoryFilter) = - runCatching { - val taskHistoryList = taskHistoryLocalDataSource.getPreviousDaysHistory(filter).getOrThrow() - return@runCatching taskHistoryList.mapToTaskHistoryList() - } + override suspend fun getPreviousDaysHistory(filter: TaskHistoryFilter) = runCatching { + val taskHistoryList = taskHistoryLocalDataSource.getPreviousDaysHistory(filter).getOrThrow() + return@runCatching taskHistoryList.mapToTaskHistoryList() + } - override suspend fun getHistory(filter: TaskHistoryFilter) = - runCatching { - val taskHistoryList = taskHistoryLocalDataSource.getHistory(filter).getOrThrow() - return@runCatching taskHistoryList.mapToTaskHistoryList() - } + override suspend fun getHistory(filter: TaskHistoryFilter) = runCatching { + val taskHistoryList = taskHistoryLocalDataSource.getHistory(filter).getOrThrow() + return@runCatching taskHistoryList.mapToTaskHistoryList() + } - override suspend fun getTaskHistory(taskId: Long) = - runCatching { - val taskHistoryList = taskHistoryLocalDataSource.getTaskHistoryByTask(taskId).getOrThrow() - return@runCatching taskHistoryList.mapToTaskHistoryList() - } + override suspend fun getTaskHistory(taskId: Long) = runCatching { + val taskHistoryList = taskHistoryLocalDataSource.getTaskHistoryByTask(taskId).getOrThrow() + return@runCatching taskHistoryList.mapToTaskHistoryList() + } override suspend fun insert( task: Task, diff --git a/feature/task-history/impl/src/main/java/br/com/sailboat/todozy/feature/task/history/impl/domain/usecase/GetTaskProgressUseCaseImpl.kt b/feature/task-history/impl/src/main/java/br/com/sailboat/todozy/feature/task/history/impl/domain/usecase/GetTaskProgressUseCaseImpl.kt index fdbf3380..1a16b160 100644 --- a/feature/task-history/impl/src/main/java/br/com/sailboat/todozy/feature/task/history/impl/domain/usecase/GetTaskProgressUseCaseImpl.kt +++ b/feature/task-history/impl/src/main/java/br/com/sailboat/todozy/feature/task/history/impl/domain/usecase/GetTaskProgressUseCaseImpl.kt @@ -17,40 +17,39 @@ internal class GetTaskProgressUseCaseImpl( private val taskHistoryRepository: TaskHistoryRepository, private val clock: Clock = Clock.systemDefaultZone(), ) : GetTaskProgressUseCase { - override suspend fun invoke(filter: TaskProgressFilter): Result> = - runCatching { - val today = LocalDate.now(clock) - val historyFilter = buildHistoryFilter(filter, today) - val history = taskHistoryRepository.getHistory(historyFilter).getOrThrow() - val historyByDate = - history.groupBy { taskHistory -> - taskHistory.insertingDate - .toDateTimeCalendar() - .toInstant() - .atZone(clock.zone) - .toLocalDate() - } + override suspend fun invoke(filter: TaskProgressFilter): Result> = runCatching { + val today = LocalDate.now(clock) + val historyFilter = buildHistoryFilter(filter, today) + val history = taskHistoryRepository.getHistory(historyFilter).getOrThrow() + val historyByDate = + history.groupBy { taskHistory -> + taskHistory.insertingDate + .toDateTimeCalendar() + .toInstant() + .atZone(clock.zone) + .toLocalDate() + } - val startDate = - when (filter.range) { - TaskProgressRange.ALL -> historyByDate.keys.minOrNull() ?: today - else -> filter.range.startDate(today) - } - val dateRange = buildDateRange(startDate, today) + val startDate = + when (filter.range) { + TaskProgressRange.ALL -> historyByDate.keys.minOrNull() ?: today + else -> filter.range.startDate(today) + } + val dateRange = buildDateRange(startDate, today) - return@runCatching dateRange.map { date -> - val entries = historyByDate[date].orEmpty() - val doneCount = entries.count { it.status == TaskStatus.DONE } - val notDoneCount = entries.count { it.status == TaskStatus.NOT_DONE } + return@runCatching dateRange.map { date -> + val entries = historyByDate[date].orEmpty() + val doneCount = entries.count { it.status == TaskStatus.DONE } + val notDoneCount = entries.count { it.status == TaskStatus.NOT_DONE } - TaskProgressDay( - date = date, - doneCount = doneCount, - notDoneCount = notDoneCount, - totalCount = entries.size, - ) - } + TaskProgressDay( + date = date, + doneCount = doneCount, + notDoneCount = notDoneCount, + totalCount = entries.size, + ) } + } private fun buildHistoryFilter( filter: TaskProgressFilter, @@ -76,10 +75,9 @@ internal class GetTaskProgressUseCaseImpl( private fun buildDateRange( startDate: LocalDate, endDate: LocalDate, - ): List = - generateSequence(startDate) { currentDate -> - currentDate.plusDays(1) - }.takeWhile { currentDate -> - currentDate.isAfter(endDate).not() - }.toList() + ): List = generateSequence(startDate) { currentDate -> + currentDate.plusDays(1) + }.takeWhile { currentDate -> + currentDate.isAfter(endDate).not() + }.toList() } diff --git a/feature/task-history/impl/src/main/java/br/com/sailboat/todozy/feature/task/history/impl/presentation/TaskHistoryFragment.kt b/feature/task-history/impl/src/main/java/br/com/sailboat/todozy/feature/task/history/impl/presentation/TaskHistoryFragment.kt index 6261bc5e..97c3e72b 100644 --- a/feature/task-history/impl/src/main/java/br/com/sailboat/todozy/feature/task/history/impl/presentation/TaskHistoryFragment.kt +++ b/feature/task-history/impl/src/main/java/br/com/sailboat/todozy/feature/task/history/impl/presentation/TaskHistoryFragment.kt @@ -127,13 +127,12 @@ internal class TaskHistoryFragment : Fragment(), SearchMenu by SearchMenuImpl() companion object { fun newInstance() = TaskHistoryFragment() - fun newInstance(taskId: Long): TaskHistoryFragment = - with(TaskHistoryFragment()) { - val bundle = Bundle() - bundle.putTaskId(taskId) - arguments = bundle - return this - } + fun newInstance(taskId: Long): TaskHistoryFragment = with(TaskHistoryFragment()) { + val bundle = Bundle() + bundle.putTaskId(taskId) + arguments = bundle + return this + } } override fun onCreateView( diff --git a/feature/task-history/impl/src/main/java/br/com/sailboat/todozy/feature/task/history/impl/presentation/dialog/TaskHistoryFilterDialog.kt b/feature/task-history/impl/src/main/java/br/com/sailboat/todozy/feature/task/history/impl/presentation/dialog/TaskHistoryFilterDialog.kt index 2005955b..1e3b4281 100644 --- a/feature/task-history/impl/src/main/java/br/com/sailboat/todozy/feature/task/history/impl/presentation/dialog/TaskHistoryFilterDialog.kt +++ b/feature/task-history/impl/src/main/java/br/com/sailboat/todozy/feature/task/history/impl/presentation/dialog/TaskHistoryFilterDialog.kt @@ -32,18 +32,17 @@ internal class TaskHistoryFilterDialog : DialogFragment() { return buildDialog() } - private fun initViews() = - with(binding) { - llDialogFilterTaskHistoryDate.setOnClickListener { - callback?.onClickFilterDate() - dialog?.dismiss() - } + private fun initViews() = with(binding) { + llDialogFilterTaskHistoryDate.setOnClickListener { + callback?.onClickFilterDate() + dialog?.dismiss() + } - llDialogFilterTaskHistoryStatus.setOnClickListener { - callback?.onClickFilterStatus() - dialog?.dismiss() - } + llDialogFilterTaskHistoryStatus.setOnClickListener { + callback?.onClickFilterStatus() + dialog?.dismiss() } + } private fun observeViewModel() { viewModel.viewState.status.observe(this) { status -> @@ -54,11 +53,10 @@ internal class TaskHistoryFilterDialog : DialogFragment() { } } - private fun buildDialog() = - AlertDialog.Builder(requireContext()).run { - setView(binding.root) - create() - } + private fun buildDialog() = AlertDialog.Builder(requireContext()).run { + setView(binding.root) + create() + } companion object { val TAG = TaskHistoryFilterDialog::class.java.name diff --git a/feature/task-history/impl/src/main/java/br/com/sailboat/todozy/feature/task/history/impl/presentation/dialog/daterange/DateRangeSelectorFilterDialog.kt b/feature/task-history/impl/src/main/java/br/com/sailboat/todozy/feature/task/history/impl/presentation/dialog/daterange/DateRangeSelectorFilterDialog.kt index 236453c0..85eb5d56 100644 --- a/feature/task-history/impl/src/main/java/br/com/sailboat/todozy/feature/task/history/impl/presentation/dialog/daterange/DateRangeSelectorFilterDialog.kt +++ b/feature/task-history/impl/src/main/java/br/com/sailboat/todozy/feature/task/history/impl/presentation/dialog/daterange/DateRangeSelectorFilterDialog.kt @@ -50,42 +50,41 @@ internal class DateRangeSelectorFilterDialog : BaseDialogFragment() { reattachPickers() } - private fun initViews() = - with(binding) { - llDateRangeSelectorInitialDate.setOnClickListener { - if (childFragmentManager.findFragmentByTag(INITIAL_DATE_SELECTOR) != null) return@setOnClickListener - - val picker = - MaterialDatePicker.Builder.datePicker() - .setTitleText(getString(UiR.string.label_starting_date)) - .setSelection(initialDate.orNewCalendar().timeInMillis) - .build() - - picker.addOnPositiveButtonClickListener { millis -> - handleInitialDateSelection(millis) - } - - picker.show(childFragmentManager, INITIAL_DATE_SELECTOR) - initialPicker = picker + private fun initViews() = with(binding) { + llDateRangeSelectorInitialDate.setOnClickListener { + if (childFragmentManager.findFragmentByTag(INITIAL_DATE_SELECTOR) != null) return@setOnClickListener + + val picker = + MaterialDatePicker.Builder.datePicker() + .setTitleText(getString(UiR.string.label_starting_date)) + .setSelection(initialDate.orNewCalendar().timeInMillis) + .build() + + picker.addOnPositiveButtonClickListener { millis -> + handleInitialDateSelection(millis) } - llDateRangeSelectorFinalDate.setOnClickListener { - if (childFragmentManager.findFragmentByTag(FINAL_DATE_SELECTOR) != null) return@setOnClickListener + picker.show(childFragmentManager, INITIAL_DATE_SELECTOR) + initialPicker = picker + } - val picker = - MaterialDatePicker.Builder.datePicker() - .setTitleText(getString(UiR.string.final_date)) - .setSelection(finalDate.orNewCalendar().timeInMillis) - .build() + llDateRangeSelectorFinalDate.setOnClickListener { + if (childFragmentManager.findFragmentByTag(FINAL_DATE_SELECTOR) != null) return@setOnClickListener - picker.addOnPositiveButtonClickListener { millis -> - handleFinalDateSelection(millis) - } + val picker = + MaterialDatePicker.Builder.datePicker() + .setTitleText(getString(UiR.string.final_date)) + .setSelection(finalDate.orNewCalendar().timeInMillis) + .build() - picker.show(childFragmentManager, FINAL_DATE_SELECTOR) - finalPicker = picker + picker.addOnPositiveButtonClickListener { millis -> + handleFinalDateSelection(millis) } + + picker.show(childFragmentManager, FINAL_DATE_SELECTOR) + finalPicker = picker } + } private fun observeViewModel() { viewModel.viewState.action.observe(this) { action -> diff --git a/feature/task-history/impl/src/main/java/br/com/sailboat/todozy/feature/task/history/impl/presentation/dialog/delete/DeleteTaskHistoryDialog.kt b/feature/task-history/impl/src/main/java/br/com/sailboat/todozy/feature/task/history/impl/presentation/dialog/delete/DeleteTaskHistoryDialog.kt index 537b4c6c..07a67710 100644 --- a/feature/task-history/impl/src/main/java/br/com/sailboat/todozy/feature/task/history/impl/presentation/dialog/delete/DeleteTaskHistoryDialog.kt +++ b/feature/task-history/impl/src/main/java/br/com/sailboat/todozy/feature/task/history/impl/presentation/dialog/delete/DeleteTaskHistoryDialog.kt @@ -19,13 +19,12 @@ class DeleteTaskHistoryDialog : BaseDialogFragment() { companion object { val TAG: String = DeleteTaskHistoryDialog::class.java.name - fun newInstance(taskHistoryPosition: Int) = - DeleteTaskHistoryDialog().apply { - arguments = - Bundle().apply { - putInt(TASK_HISTORY_POSITION, taskHistoryPosition) - } - } + fun newInstance(taskHistoryPosition: Int) = DeleteTaskHistoryDialog().apply { + arguments = + Bundle().apply { + putInt(TASK_HISTORY_POSITION, taskHistoryPosition) + } + } } override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { diff --git a/feature/task-history/impl/src/main/java/br/com/sailboat/todozy/feature/task/history/impl/presentation/viewmodel/TaskHistoryViewModel.kt b/feature/task-history/impl/src/main/java/br/com/sailboat/todozy/feature/task/history/impl/presentation/viewmodel/TaskHistoryViewModel.kt index a6e51bae..4bbf5529 100644 --- a/feature/task-history/impl/src/main/java/br/com/sailboat/todozy/feature/task/history/impl/presentation/viewmodel/TaskHistoryViewModel.kt +++ b/feature/task-history/impl/src/main/java/br/com/sailboat/todozy/feature/task/history/impl/presentation/viewmodel/TaskHistoryViewModel.kt @@ -124,17 +124,16 @@ internal class TaskHistoryViewModel( viewState.viewAction.value = TaskHistoryViewAction.NavigateToClearAllHistoryConfirmation } - private fun onClickConfirmClearAllHistory() = - viewModelScope.launch { - try { - clearHistorySelectedPosition() - deleteAllHistoryUseCase() - loadHistoryTasks() - } catch (e: Exception) { - logService.error(e) - viewState.viewAction.value = TaskHistoryViewAction.ShowGenericError - } + private fun onClickConfirmClearAllHistory() = viewModelScope.launch { + try { + clearHistorySelectedPosition() + deleteAllHistoryUseCase() + loadHistoryTasks() + } catch (e: Exception) { + logService.error(e) + viewState.viewAction.value = TaskHistoryViewAction.ShowGenericError } + } private fun onClickDeleteTaskHistoryItem(viewAction: TaskHistoryViewIntent.OnClickDeleteTaskHistoryItem) { viewState.viewAction.value = TaskHistoryViewAction.NavigateToDeleteTaskHistoryConfirmation(viewAction.position) @@ -277,43 +276,42 @@ internal class TaskHistoryViewModel( loadHistoryTasks() } - private fun loadHistoryTasks() = - viewModelScope.launch { - try { - viewState.loading.postValue(true) - - val taskMetrics = async { getTaskMetricsUseCase(filter).getOrNull() } - - val taskHistoryCategories = - listOf( - TaskHistoryCategory.TODAY, - TaskHistoryCategory.YESTERDAY, - TaskHistoryCategory.PREVIOUS_DAYS, - ) - - val taskHistoryList = - taskHistoryCategories.map { category -> - async { - val searchFilter = - filter.copy( - text = filter.text, - category = category, - ) - val taskHistoryList = getTaskHistoryUseCase(searchFilter).getOrThrow() - - taskHistoryUiModelFactory.create(taskHistoryList, category) - } - }.awaitAll().flatten() - - viewState.taskMetrics.postValue(taskMetrics.await()) - viewState.taskHistoryList.postValue(taskHistoryList) - } catch (e: Exception) { - logService.error(e) - viewState.viewAction.value = TaskHistoryViewAction.ShowGenericError - } finally { - viewState.loading.postValue(false) - } + private fun loadHistoryTasks() = viewModelScope.launch { + try { + viewState.loading.postValue(true) + + val taskMetrics = async { getTaskMetricsUseCase(filter).getOrNull() } + + val taskHistoryCategories = + listOf( + TaskHistoryCategory.TODAY, + TaskHistoryCategory.YESTERDAY, + TaskHistoryCategory.PREVIOUS_DAYS, + ) + + val taskHistoryList = + taskHistoryCategories.map { category -> + async { + val searchFilter = + filter.copy( + text = filter.text, + category = category, + ) + val taskHistoryList = getTaskHistoryUseCase(searchFilter).getOrThrow() + + taskHistoryUiModelFactory.create(taskHistoryList, category) + } + }.awaitAll().flatten() + + viewState.taskMetrics.postValue(taskMetrics.await()) + viewState.taskHistoryList.postValue(taskHistoryList) + } catch (e: Exception) { + logService.error(e) + viewState.viewAction.value = TaskHistoryViewAction.ShowGenericError + } finally { + viewState.loading.postValue(false) } + } private fun clearSubtitle() { viewState.subtitle.value = "" @@ -323,12 +321,11 @@ internal class TaskHistoryViewModel( viewState.subtitle.value = getDateFilterNameViewUseCase(dateFilterType) } - private fun setDateRangeSubtitle() = - with(filter) { - val initial = initialDate?.run { calendarService.getShortDate(this) }.orEmpty() - val final = finalDate?.run { calendarService.getShortDate(this) }.orEmpty() - viewState.subtitle.value = "$initial - $final" - } + private fun setDateRangeSubtitle() = with(filter) { + val initial = initialDate?.run { calendarService.getShortDate(this) }.orEmpty() + val final = finalDate?.run { calendarService.getShortDate(this) }.orEmpty() + viewState.subtitle.value = "$initial - $final" + } private fun updateHistoryStatus( position: Int, diff --git a/feature/task-history/impl/src/test/java/br/com/sailboat/todozy/feature/task/history/impl/data/repository/TaskHistoryRepositoryImplTest.kt b/feature/task-history/impl/src/test/java/br/com/sailboat/todozy/feature/task/history/impl/data/repository/TaskHistoryRepositoryImplTest.kt index b796899f..ee8c7f3d 100644 --- a/feature/task-history/impl/src/test/java/br/com/sailboat/todozy/feature/task/history/impl/data/repository/TaskHistoryRepositoryImplTest.kt +++ b/feature/task-history/impl/src/test/java/br/com/sailboat/todozy/feature/task/history/impl/data/repository/TaskHistoryRepositoryImplTest.kt @@ -25,197 +25,186 @@ internal class TaskHistoryRepositoryImplTest { ) @Test - fun `should call getTotalOfNotDoneTasks from local data source when getTotalOfNotDoneTasks is called from repository`() = - runBlocking { - val taskHistoryFilter = TaskHistoryFilter() - val totalOfNotDoneTasks = 25 - prepareScenario(totalOfNotDoneTasks = Result.success(totalOfNotDoneTasks)) + fun `should call getTotalOfNotDoneTasks from local data source when getTotalOfNotDoneTasks is called from repository`() = runBlocking { + val taskHistoryFilter = TaskHistoryFilter() + val totalOfNotDoneTasks = 25 + prepareScenario(totalOfNotDoneTasks = Result.success(totalOfNotDoneTasks)) - val result = taskHistoryRepository.getTotalOfNotDoneTasks(taskHistoryFilter).getOrNull() + val result = taskHistoryRepository.getTotalOfNotDoneTasks(taskHistoryFilter).getOrNull() - assertEquals(totalOfNotDoneTasks, result) - coVerify { taskHistoryLocalDataSource.getTotalOfNotDoneTasks(taskHistoryFilter) } - } + assertEquals(totalOfNotDoneTasks, result) + coVerify { taskHistoryLocalDataSource.getTotalOfNotDoneTasks(taskHistoryFilter) } + } @Test - fun `should call getTotalOfDoneTasks from local data source when getTotalOfDoneTasks is called from repository`() = - runBlocking { - val taskHistoryFilter = TaskHistoryFilter() - val totalOfDoneTasks = 25 - prepareScenario(totalOfDoneTasks = Result.success(totalOfDoneTasks)) + fun `should call getTotalOfDoneTasks from local data source when getTotalOfDoneTasks is called from repository`() = runBlocking { + val taskHistoryFilter = TaskHistoryFilter() + val totalOfDoneTasks = 25 + prepareScenario(totalOfDoneTasks = Result.success(totalOfDoneTasks)) - val result = taskHistoryRepository.getTotalOfDoneTasks(taskHistoryFilter).getOrNull() + val result = taskHistoryRepository.getTotalOfDoneTasks(taskHistoryFilter).getOrNull() - assertEquals(totalOfDoneTasks, result) - coVerify { taskHistoryLocalDataSource.getTotalOfDoneTasks(taskHistoryFilter) } - } + assertEquals(totalOfDoneTasks, result) + coVerify { taskHistoryLocalDataSource.getTotalOfDoneTasks(taskHistoryFilter) } + } @Test - fun `should call getTodayHistory from local data source when getTodayHistory is called from repository`() = - runBlocking { - val taskHistoryData = makeTaskHistoryData() - val taskHistoryFilter = TaskHistoryFilter() - prepareScenario(taskHistoryDataList = Result.success(listOf(taskHistoryData))) - - val result = taskHistoryRepository.getTodayHistory(taskHistoryFilter).getOrNull() - - val expected = - listOf( - TaskHistory( - id = taskHistoryData.id, - taskId = taskHistoryData.taskId, - taskName = taskHistoryData.taskName.orEmpty(), - status = TaskStatus.DONE, - insertingDate = taskHistoryData.insertingDate.orEmpty(), - ), - ) - assertEquals(expected, result) - coVerify { taskHistoryLocalDataSource.getTodayHistory(taskHistoryFilter) } - } + fun `should call getTodayHistory from local data source when getTodayHistory is called from repository`() = runBlocking { + val taskHistoryData = makeTaskHistoryData() + val taskHistoryFilter = TaskHistoryFilter() + prepareScenario(taskHistoryDataList = Result.success(listOf(taskHistoryData))) + + val result = taskHistoryRepository.getTodayHistory(taskHistoryFilter).getOrNull() + + val expected = + listOf( + TaskHistory( + id = taskHistoryData.id, + taskId = taskHistoryData.taskId, + taskName = taskHistoryData.taskName.orEmpty(), + status = TaskStatus.DONE, + insertingDate = taskHistoryData.insertingDate.orEmpty(), + ), + ) + assertEquals(expected, result) + coVerify { taskHistoryLocalDataSource.getTodayHistory(taskHistoryFilter) } + } @Test - fun `should call getYesterdayHistory from local data source when getYesterdayHistory is called from repository`() = - runBlocking { - val taskHistoryData = makeTaskHistoryData() - val taskHistoryFilter = TaskHistoryFilter() - prepareScenario(taskHistoryDataList = Result.success(listOf(taskHistoryData))) - - val result = taskHistoryRepository.getYesterdayHistory(taskHistoryFilter).getOrNull() - - val expected = - listOf( - TaskHistory( - id = taskHistoryData.id, - taskId = taskHistoryData.taskId, - taskName = taskHistoryData.taskName.orEmpty(), - status = TaskStatus.DONE, - insertingDate = taskHistoryData.insertingDate.orEmpty(), - ), - ) - assertEquals(expected, result) - coVerify { taskHistoryLocalDataSource.getYesterdayHistory(taskHistoryFilter) } - } + fun `should call getYesterdayHistory from local data source when getYesterdayHistory is called from repository`() = runBlocking { + val taskHistoryData = makeTaskHistoryData() + val taskHistoryFilter = TaskHistoryFilter() + prepareScenario(taskHistoryDataList = Result.success(listOf(taskHistoryData))) + + val result = taskHistoryRepository.getYesterdayHistory(taskHistoryFilter).getOrNull() + + val expected = + listOf( + TaskHistory( + id = taskHistoryData.id, + taskId = taskHistoryData.taskId, + taskName = taskHistoryData.taskName.orEmpty(), + status = TaskStatus.DONE, + insertingDate = taskHistoryData.insertingDate.orEmpty(), + ), + ) + assertEquals(expected, result) + coVerify { taskHistoryLocalDataSource.getYesterdayHistory(taskHistoryFilter) } + } @Test - fun `should call getPreviousDaysHistory from local data source when getPreviousDaysHistory is called from repository`() = - runBlocking { - val taskHistoryData = makeTaskHistoryData() - val taskHistoryFilter = TaskHistoryFilter() - prepareScenario(taskHistoryDataList = Result.success(listOf(taskHistoryData))) - - val result = taskHistoryRepository.getPreviousDaysHistory(taskHistoryFilter).getOrNull() - - val expected = - listOf( - TaskHistory( - id = taskHistoryData.id, - taskId = taskHistoryData.taskId, - taskName = taskHistoryData.taskName.orEmpty(), - status = TaskStatus.DONE, - insertingDate = taskHistoryData.insertingDate.orEmpty(), - ), - ) - assertEquals(expected, result) - coVerify { taskHistoryLocalDataSource.getPreviousDaysHistory(taskHistoryFilter) } - } + fun `should call getPreviousDaysHistory from local data source when getPreviousDaysHistory is called from repository`() = runBlocking { + val taskHistoryData = makeTaskHistoryData() + val taskHistoryFilter = TaskHistoryFilter() + prepareScenario(taskHistoryDataList = Result.success(listOf(taskHistoryData))) + + val result = taskHistoryRepository.getPreviousDaysHistory(taskHistoryFilter).getOrNull() + + val expected = + listOf( + TaskHistory( + id = taskHistoryData.id, + taskId = taskHistoryData.taskId, + taskName = taskHistoryData.taskName.orEmpty(), + status = TaskStatus.DONE, + insertingDate = taskHistoryData.insertingDate.orEmpty(), + ), + ) + assertEquals(expected, result) + coVerify { taskHistoryLocalDataSource.getPreviousDaysHistory(taskHistoryFilter) } + } @Test - fun `should call getHistory from local data source when getHistory is called from repository`() = - runBlocking { - val taskHistoryData = makeTaskHistoryData() - val taskHistoryFilter = TaskHistoryFilter() - prepareScenario(taskHistoryDataList = Result.success(listOf(taskHistoryData))) - - val result = taskHistoryRepository.getHistory(taskHistoryFilter).getOrNull() - - val expected = - listOf( - TaskHistory( - id = taskHistoryData.id, - taskId = taskHistoryData.taskId, - taskName = taskHistoryData.taskName.orEmpty(), - status = TaskStatus.DONE, - insertingDate = taskHistoryData.insertingDate.orEmpty(), - ), - ) - assertEquals(expected, result) - coVerify { taskHistoryLocalDataSource.getHistory(taskHistoryFilter) } - } + fun `should call getHistory from local data source when getHistory is called from repository`() = runBlocking { + val taskHistoryData = makeTaskHistoryData() + val taskHistoryFilter = TaskHistoryFilter() + prepareScenario(taskHistoryDataList = Result.success(listOf(taskHistoryData))) + + val result = taskHistoryRepository.getHistory(taskHistoryFilter).getOrNull() + + val expected = + listOf( + TaskHistory( + id = taskHistoryData.id, + taskId = taskHistoryData.taskId, + taskName = taskHistoryData.taskName.orEmpty(), + status = TaskStatus.DONE, + insertingDate = taskHistoryData.insertingDate.orEmpty(), + ), + ) + assertEquals(expected, result) + coVerify { taskHistoryLocalDataSource.getHistory(taskHistoryFilter) } + } @Test - fun `should call getTaskHistoryByTask from local data source when getTaskHistory is called from repository`() = - runBlocking { - val taskHistoryData = makeTaskHistoryData() - prepareScenario(taskHistoryDataList = Result.success(listOf(taskHistoryData))) - - val result = taskHistoryRepository.getTaskHistory(taskHistoryData.taskId).getOrNull() - - val expected = - listOf( - TaskHistory( - id = taskHistoryData.id, - taskId = taskHistoryData.taskId, - taskName = taskHistoryData.taskName.orEmpty(), - status = TaskStatus.DONE, - insertingDate = taskHistoryData.insertingDate.orEmpty(), - ), - ) - assertEquals(expected, result) - coVerify { taskHistoryLocalDataSource.getTaskHistoryByTask(taskHistoryData.taskId) } - } + fun `should call getTaskHistoryByTask from local data source when getTaskHistory is called from repository`() = runBlocking { + val taskHistoryData = makeTaskHistoryData() + prepareScenario(taskHistoryDataList = Result.success(listOf(taskHistoryData))) + + val result = taskHistoryRepository.getTaskHistory(taskHistoryData.taskId).getOrNull() + + val expected = + listOf( + TaskHistory( + id = taskHistoryData.id, + taskId = taskHistoryData.taskId, + taskName = taskHistoryData.taskName.orEmpty(), + status = TaskStatus.DONE, + insertingDate = taskHistoryData.insertingDate.orEmpty(), + ), + ) + assertEquals(expected, result) + coVerify { taskHistoryLocalDataSource.getTaskHistoryByTask(taskHistoryData.taskId) } + } @Test - fun `should call save from local data source when insert is called from repository`() = - runBlocking { - val task = makeTask() - prepareScenario() + fun `should call save from local data source when insert is called from repository`() = runBlocking { + val task = makeTask() + prepareScenario() - taskHistoryRepository.insert(task, TaskStatus.DONE) + taskHistoryRepository.insert(task, TaskStatus.DONE) - coVerify { taskHistoryLocalDataSource.save(task.id, 1) } - } + coVerify { taskHistoryLocalDataSource.save(task.id, 1) } + } @Test - fun `should call update from local data source when update is called from repository`() = - runBlocking { - val taskHistory = makeTaskHistory() - val taskHistoryData = - TaskHistoryData( - id = taskHistory.id, - taskId = taskHistory.taskId, - taskName = taskHistory.taskName, - status = 1, - insertingDate = taskHistory.insertingDate, - enabled = true, - ) - prepareScenario() - - taskHistoryRepository.update(taskHistory) - - coVerify { taskHistoryLocalDataSource.update(taskHistoryData) } - } + fun `should call update from local data source when update is called from repository`() = runBlocking { + val taskHistory = makeTaskHistory() + val taskHistoryData = + TaskHistoryData( + id = taskHistory.id, + taskId = taskHistory.taskId, + taskName = taskHistory.taskName, + status = 1, + insertingDate = taskHistory.insertingDate, + enabled = true, + ) + prepareScenario() + + taskHistoryRepository.update(taskHistory) + + coVerify { taskHistoryLocalDataSource.update(taskHistoryData) } + } @Test - fun `should call delete from local data source when delete is called from repository`() = - runBlocking { - val taskHistory = makeTaskHistory() - prepareScenario() + fun `should call delete from local data source when delete is called from repository`() = runBlocking { + val taskHistory = makeTaskHistory() + prepareScenario() - taskHistoryRepository.delete(taskHistory) + taskHistoryRepository.delete(taskHistory) - coVerify { taskHistoryLocalDataSource.delete(taskHistory.id) } - } + coVerify { taskHistoryLocalDataSource.delete(taskHistory.id) } + } @Test - fun `should call deleteAllHistory from local data source when deleteAll is called from repository`() = - runBlocking { - val taskHistory = makeTaskHistory() - prepareScenario() + fun `should call deleteAllHistory from local data source when deleteAll is called from repository`() = runBlocking { + val taskHistory = makeTaskHistory() + prepareScenario() - taskHistoryRepository.deleteAll() + taskHistoryRepository.deleteAll() - coVerify { taskHistoryLocalDataSource.deleteAllHistory() } - } + coVerify { taskHistoryLocalDataSource.deleteAllHistory() } + } private fun prepareScenario( totalOfNotDoneTasks: Result = Result.success(10), diff --git a/feature/task-history/impl/src/test/java/br/com/sailboat/todozy/feature/task/history/impl/domain/usecase/AddHistoryUseCaseImplTest.kt b/feature/task-history/impl/src/test/java/br/com/sailboat/todozy/feature/task/history/impl/domain/usecase/AddHistoryUseCaseImplTest.kt index 97ec925f..cbe0d073 100644 --- a/feature/task-history/impl/src/test/java/br/com/sailboat/todozy/feature/task/history/impl/domain/usecase/AddHistoryUseCaseImplTest.kt +++ b/feature/task-history/impl/src/test/java/br/com/sailboat/todozy/feature/task/history/impl/domain/usecase/AddHistoryUseCaseImplTest.kt @@ -18,19 +18,18 @@ internal class AddHistoryUseCaseImplTest { private val addHistoryUseCase = AddHistoryUseCaseImpl(repository) @Test - fun `should insert history in the repository`() = - runBlocking { - val dateTime = Calendar.getInstance() - val alarm = - Alarm( - dateTime = dateTime, - repeatType = RepeatType.DAY, - ) - val task = Task(id = 45, name = "Task Name", notes = "Some notes", alarm = alarm) + fun `should insert history in the repository`() = runBlocking { + val dateTime = Calendar.getInstance() + val alarm = + Alarm( + dateTime = dateTime, + repeatType = RepeatType.DAY, + ) + val task = Task(id = 45, name = "Task Name", notes = "Some notes", alarm = alarm) - addHistoryUseCase(task, TaskStatus.DONE) + addHistoryUseCase(task, TaskStatus.DONE) - coVerify { repository.insert(task, TaskStatus.DONE) } - confirmVerified(repository) - } + coVerify { repository.insert(task, TaskStatus.DONE) } + confirmVerified(repository) + } } diff --git a/feature/task-history/impl/src/test/java/br/com/sailboat/todozy/feature/task/history/impl/domain/usecase/DeleteAllHistoryUseCaseImplTest.kt b/feature/task-history/impl/src/test/java/br/com/sailboat/todozy/feature/task/history/impl/domain/usecase/DeleteAllHistoryUseCaseImplTest.kt index 0f7797cc..7c43f2c4 100644 --- a/feature/task-history/impl/src/test/java/br/com/sailboat/todozy/feature/task/history/impl/domain/usecase/DeleteAllHistoryUseCaseImplTest.kt +++ b/feature/task-history/impl/src/test/java/br/com/sailboat/todozy/feature/task/history/impl/domain/usecase/DeleteAllHistoryUseCaseImplTest.kt @@ -13,11 +13,10 @@ internal class DeleteAllHistoryUseCaseImplTest { private val deleteAllHistoryUseCase = DeleteAllHistoryUseCaseImpl(repository) @Test - fun `should delete all history from repository`() = - runBlocking { - deleteAllHistoryUseCase() + fun `should delete all history from repository`() = runBlocking { + deleteAllHistoryUseCase() - coVerify { repository.deleteAll() } - confirmVerified(repository) - } + coVerify { repository.deleteAll() } + confirmVerified(repository) + } } diff --git a/feature/task-history/impl/src/test/java/br/com/sailboat/todozy/feature/task/history/impl/domain/usecase/DeleteHistoryUseCaseImplTest.kt b/feature/task-history/impl/src/test/java/br/com/sailboat/todozy/feature/task/history/impl/domain/usecase/DeleteHistoryUseCaseImplTest.kt index bc15b7fd..f308337e 100644 --- a/feature/task-history/impl/src/test/java/br/com/sailboat/todozy/feature/task/history/impl/domain/usecase/DeleteHistoryUseCaseImplTest.kt +++ b/feature/task-history/impl/src/test/java/br/com/sailboat/todozy/feature/task/history/impl/domain/usecase/DeleteHistoryUseCaseImplTest.kt @@ -15,18 +15,17 @@ internal class DeleteHistoryUseCaseImplTest { private val deleteHistoryUseCase = DeleteHistoryUseCaseImpl(repository) @Test - fun `should delete history from repository`() = - runBlocking { - val history = - TaskHistory( - taskId = 12, - status = TaskStatus.DONE, - insertingDate = "2020-08-29-16-18-00", - taskName = "Task 1", - ) - deleteHistoryUseCase(history) + fun `should delete history from repository`() = runBlocking { + val history = + TaskHistory( + taskId = 12, + status = TaskStatus.DONE, + insertingDate = "2020-08-29-16-18-00", + taskName = "Task 1", + ) + deleteHistoryUseCase(history) - coVerify { repository.delete(history) } - confirmVerified(repository) - } + coVerify { repository.delete(history) } + confirmVerified(repository) + } } diff --git a/feature/task-history/impl/src/test/java/br/com/sailboat/todozy/feature/task/history/impl/domain/usecase/GetTaskHistoryUseCaseImplTest.kt b/feature/task-history/impl/src/test/java/br/com/sailboat/todozy/feature/task/history/impl/domain/usecase/GetTaskHistoryUseCaseImplTest.kt index 67bcce0c..7ed91105 100644 --- a/feature/task-history/impl/src/test/java/br/com/sailboat/todozy/feature/task/history/impl/domain/usecase/GetTaskHistoryUseCaseImplTest.kt +++ b/feature/task-history/impl/src/test/java/br/com/sailboat/todozy/feature/task/history/impl/domain/usecase/GetTaskHistoryUseCaseImplTest.kt @@ -19,38 +19,10 @@ internal class GetTaskHistoryUseCaseImplTest { private val getTaskHistoryUseCase = GetTaskHistoryUseCaseImpl(repository) @Test - fun `should get task history from previous days from repository`() = - runBlocking { - val filter = TaskHistoryFilter(category = TaskHistoryCategory.PREVIOUS_DAYS) - val taskHistoryListResult = - Result.success( - listOf( - TaskHistory( - id = 2, - taskId = 22, - taskName = "Task 1", - status = TaskStatus.DONE, - insertingDate = "2020-08-29-13-26-56", - ), - ), - ) - prepareScenario(taskHistoryListResult = taskHistoryListResult) - - val result: Result> = getTaskHistoryUseCase(filter) - - coVerify(exactly = 1) { repository.getPreviousDaysHistory(any()) } - coVerify(exactly = 0) { repository.getYesterdayHistory(any()) } - coVerify(exactly = 0) { repository.getTodayHistory(any()) } - - confirmVerified(repository) - assertEquals(taskHistoryListResult, result) - } - - @Test - fun `should get task history from yesterday from repository`() = - runBlocking { - val filter = TaskHistoryFilter(category = TaskHistoryCategory.YESTERDAY) - val taskHistoryList = + fun `should get task history from previous days from repository`() = runBlocking { + val filter = TaskHistoryFilter(category = TaskHistoryCategory.PREVIOUS_DAYS) + val taskHistoryListResult = + Result.success( listOf( TaskHistory( id = 2, @@ -59,44 +31,69 @@ internal class GetTaskHistoryUseCaseImplTest { status = TaskStatus.DONE, insertingDate = "2020-08-29-13-26-56", ), - ) - prepareScenario(taskHistoryListResult = Result.success(taskHistoryList)) + ), + ) + prepareScenario(taskHistoryListResult = taskHistoryListResult) - val result = getTaskHistoryUseCase(filter).getOrNull() + val result: Result> = getTaskHistoryUseCase(filter) - coVerify(exactly = 0) { repository.getPreviousDaysHistory(any()) } - coVerify(exactly = 1) { repository.getYesterdayHistory(any()) } - coVerify(exactly = 0) { repository.getTodayHistory(any()) } + coVerify(exactly = 1) { repository.getPreviousDaysHistory(any()) } + coVerify(exactly = 0) { repository.getYesterdayHistory(any()) } + coVerify(exactly = 0) { repository.getTodayHistory(any()) } - confirmVerified(repository) - assertEquals(taskHistoryList, result) - } + confirmVerified(repository) + assertEquals(taskHistoryListResult, result) + } @Test - fun `should get task history from today from repository`() = - runBlocking { - val filter = TaskHistoryFilter(category = TaskHistoryCategory.TODAY) - val taskHistoryList = - listOf( - TaskHistory( - id = 2, - taskId = 22, - taskName = "Task 1", - status = TaskStatus.DONE, - insertingDate = "2020-08-29-13-26-56", - ), - ) - prepareScenario(taskHistoryListResult = Result.success(taskHistoryList)) + fun `should get task history from yesterday from repository`() = runBlocking { + val filter = TaskHistoryFilter(category = TaskHistoryCategory.YESTERDAY) + val taskHistoryList = + listOf( + TaskHistory( + id = 2, + taskId = 22, + taskName = "Task 1", + status = TaskStatus.DONE, + insertingDate = "2020-08-29-13-26-56", + ), + ) + prepareScenario(taskHistoryListResult = Result.success(taskHistoryList)) + + val result = getTaskHistoryUseCase(filter).getOrNull() - val result = getTaskHistoryUseCase(filter).getOrNull() + coVerify(exactly = 0) { repository.getPreviousDaysHistory(any()) } + coVerify(exactly = 1) { repository.getYesterdayHistory(any()) } + coVerify(exactly = 0) { repository.getTodayHistory(any()) } - coVerify(exactly = 0) { repository.getPreviousDaysHistory(any()) } - coVerify(exactly = 0) { repository.getYesterdayHistory(any()) } - coVerify(exactly = 1) { repository.getTodayHistory(any()) } + confirmVerified(repository) + assertEquals(taskHistoryList, result) + } - confirmVerified(repository) - assertEquals(taskHistoryList, result) - } + @Test + fun `should get task history from today from repository`() = runBlocking { + val filter = TaskHistoryFilter(category = TaskHistoryCategory.TODAY) + val taskHistoryList = + listOf( + TaskHistory( + id = 2, + taskId = 22, + taskName = "Task 1", + status = TaskStatus.DONE, + insertingDate = "2020-08-29-13-26-56", + ), + ) + prepareScenario(taskHistoryListResult = Result.success(taskHistoryList)) + + val result = getTaskHistoryUseCase(filter).getOrNull() + + coVerify(exactly = 0) { repository.getPreviousDaysHistory(any()) } + coVerify(exactly = 0) { repository.getYesterdayHistory(any()) } + coVerify(exactly = 1) { repository.getTodayHistory(any()) } + + confirmVerified(repository) + assertEquals(taskHistoryList, result) + } private fun prepareScenario( taskHistoryListResult: Result> = diff --git a/feature/task-history/impl/src/test/java/br/com/sailboat/todozy/feature/task/history/impl/domain/usecase/GetTaskProgressUseCaseImplTest.kt b/feature/task-history/impl/src/test/java/br/com/sailboat/todozy/feature/task/history/impl/domain/usecase/GetTaskProgressUseCaseImplTest.kt index 41309f1c..8b1e4823 100644 --- a/feature/task-history/impl/src/test/java/br/com/sailboat/todozy/feature/task/history/impl/domain/usecase/GetTaskProgressUseCaseImplTest.kt +++ b/feature/task-history/impl/src/test/java/br/com/sailboat/todozy/feature/task/history/impl/domain/usecase/GetTaskProgressUseCaseImplTest.kt @@ -24,108 +24,106 @@ internal class GetTaskProgressUseCaseImplTest { private val getTaskProgressUseCase = GetTaskProgressUseCaseImpl(taskHistoryRepository, clock) @Test - fun `should map progress grouped by day within selected range`() = - runTest { - val filter = TaskProgressFilter(range = TaskProgressRange.LAST_7_DAYS, taskId = 10L) - val history = - listOf( - TaskHistory( - id = 1, - taskId = 10L, - taskName = "Task", - status = TaskStatus.DONE, - insertingDate = "2024-08-14 08:00:00", - ), - TaskHistory( - id = 2, - taskId = 10L, - taskName = "Task", - status = TaskStatus.NOT_DONE, - insertingDate = "2024-08-13 08:00:00", - ), - TaskHistory( - id = 3, - taskId = 10L, - taskName = "Task", - status = TaskStatus.DONE, - insertingDate = "2024-08-13 10:00:00", - ), - ) - - coEvery { taskHistoryRepository.getHistory(any()) } returns Result.success(history) - - val progressDays = getTaskProgressUseCase(filter).getOrThrow() - - assertEquals(7, progressDays.size) - - val august14 = progressDays.first { it.date == LocalDate.of(2024, 8, 14) } - assertEquals(1, august14.doneCount) - assertEquals(0, august14.notDoneCount) - assertEquals(1, august14.totalCount) - - val august13 = progressDays.first { it.date == LocalDate.of(2024, 8, 13) } - assertEquals(1, august13.doneCount) - assertEquals(1, august13.notDoneCount) - assertEquals(2, august13.totalCount) - - val august12 = progressDays.first { it.date == LocalDate.of(2024, 8, 12) } - assertEquals(0, august12.doneCount) - assertEquals(0, august12.notDoneCount) - assertEquals(0, august12.totalCount) - - coVerify { - taskHistoryRepository.getHistory( - withArg { - assertEquals(10L, it.taskId) - assertEquals(LocalDate.of(2024, 8, 9), it.initialDate?.toInstant()?.atZone(ZoneId.of("UTC"))?.toLocalDate()) - assertEquals(LocalDate.of(2024, 8, 15), it.finalDate?.toInstant()?.atZone(ZoneId.of("UTC"))?.toLocalDate()) - }, - ) - } + fun `should map progress grouped by day within selected range`() = runTest { + val filter = TaskProgressFilter(range = TaskProgressRange.LAST_7_DAYS, taskId = 10L) + val history = + listOf( + TaskHistory( + id = 1, + taskId = 10L, + taskName = "Task", + status = TaskStatus.DONE, + insertingDate = "2024-08-14 08:00:00", + ), + TaskHistory( + id = 2, + taskId = 10L, + taskName = "Task", + status = TaskStatus.NOT_DONE, + insertingDate = "2024-08-13 08:00:00", + ), + TaskHistory( + id = 3, + taskId = 10L, + taskName = "Task", + status = TaskStatus.DONE, + insertingDate = "2024-08-13 10:00:00", + ), + ) + + coEvery { taskHistoryRepository.getHistory(any()) } returns Result.success(history) + + val progressDays = getTaskProgressUseCase(filter).getOrThrow() + + assertEquals(7, progressDays.size) + + val august14 = progressDays.first { it.date == LocalDate.of(2024, 8, 14) } + assertEquals(1, august14.doneCount) + assertEquals(0, august14.notDoneCount) + assertEquals(1, august14.totalCount) + + val august13 = progressDays.first { it.date == LocalDate.of(2024, 8, 13) } + assertEquals(1, august13.doneCount) + assertEquals(1, august13.notDoneCount) + assertEquals(2, august13.totalCount) + + val august12 = progressDays.first { it.date == LocalDate.of(2024, 8, 12) } + assertEquals(0, august12.doneCount) + assertEquals(0, august12.notDoneCount) + assertEquals(0, august12.totalCount) + + coVerify { + taskHistoryRepository.getHistory( + withArg { + assertEquals(10L, it.taskId) + assertEquals(LocalDate.of(2024, 8, 9), it.initialDate?.toInstant()?.atZone(ZoneId.of("UTC"))?.toLocalDate()) + assertEquals(LocalDate.of(2024, 8, 15), it.finalDate?.toInstant()?.atZone(ZoneId.of("UTC"))?.toLocalDate()) + }, + ) } + } @Test - fun `should map progress from earliest history when range is all`() = - runTest { - val filter = TaskProgressFilter(range = TaskProgressRange.ALL, taskId = 5L) - val history = - listOf( - TaskHistory( - id = 1, - taskId = 5L, - taskName = "Task", - status = TaskStatus.DONE, - insertingDate = "2024-08-10 08:00:00", - ), - TaskHistory( - id = 2, - taskId = 5L, - taskName = "Task", - status = TaskStatus.NOT_DONE, - insertingDate = "2024-08-12 09:00:00", - ), - ) - - coEvery { taskHistoryRepository.getHistory(any()) } returns Result.success(history) - - val progressDays = getTaskProgressUseCase(filter).getOrThrow() - - assertEquals(LocalDate.of(2024, 8, 10), progressDays.first().date) - assertEquals(LocalDate.of(2024, 8, 15), progressDays.last().date) - - val august10 = progressDays.first { it.date == LocalDate.of(2024, 8, 10) } - assertEquals(1, august10.doneCount) - assertEquals(0, august10.notDoneCount) - assertEquals(1, august10.totalCount) - - coVerify { - taskHistoryRepository.getHistory( - withArg { - assertNull(it.initialDate) - assertNull(it.finalDate) - assertEquals(5L, it.taskId) - }, - ) - } + fun `should map progress from earliest history when range is all`() = runTest { + val filter = TaskProgressFilter(range = TaskProgressRange.ALL, taskId = 5L) + val history = + listOf( + TaskHistory( + id = 1, + taskId = 5L, + taskName = "Task", + status = TaskStatus.DONE, + insertingDate = "2024-08-10 08:00:00", + ), + TaskHistory( + id = 2, + taskId = 5L, + taskName = "Task", + status = TaskStatus.NOT_DONE, + insertingDate = "2024-08-12 09:00:00", + ), + ) + + coEvery { taskHistoryRepository.getHistory(any()) } returns Result.success(history) + + val progressDays = getTaskProgressUseCase(filter).getOrThrow() + + assertEquals(LocalDate.of(2024, 8, 10), progressDays.first().date) + assertEquals(LocalDate.of(2024, 8, 15), progressDays.last().date) + + val august10 = progressDays.first { it.date == LocalDate.of(2024, 8, 10) } + assertEquals(1, august10.doneCount) + assertEquals(0, august10.notDoneCount) + assertEquals(1, august10.totalCount) + + coVerify { + taskHistoryRepository.getHistory( + withArg { + assertNull(it.initialDate) + assertNull(it.finalDate) + assertEquals(5L, it.taskId) + }, + ) } + } } diff --git a/feature/task-history/impl/src/test/java/br/com/sailboat/todozy/feature/task/history/impl/domain/usecase/UpdateHistoryUseCaseImplTest.kt b/feature/task-history/impl/src/test/java/br/com/sailboat/todozy/feature/task/history/impl/domain/usecase/UpdateHistoryUseCaseImplTest.kt index 256bf888..de92f314 100644 --- a/feature/task-history/impl/src/test/java/br/com/sailboat/todozy/feature/task/history/impl/domain/usecase/UpdateHistoryUseCaseImplTest.kt +++ b/feature/task-history/impl/src/test/java/br/com/sailboat/todozy/feature/task/history/impl/domain/usecase/UpdateHistoryUseCaseImplTest.kt @@ -15,18 +15,17 @@ internal class UpdateHistoryUseCaseImplTest { private val updateHistoryUseCase = UpdateHistoryUseCaseImpl(repository) @Test - fun `should delete history from repository`() = - runBlocking { - val history = - TaskHistory( - taskId = 12, - status = TaskStatus.DONE, - insertingDate = "2020-08-29-16-18-00", - taskName = "Task 1", - ) - updateHistoryUseCase(history) + fun `should delete history from repository`() = runBlocking { + val history = + TaskHistory( + taskId = 12, + status = TaskStatus.DONE, + insertingDate = "2020-08-29-16-18-00", + taskName = "Task 1", + ) + updateHistoryUseCase(history) - coVerify { repository.update(history) } - confirmVerified(repository) - } + coVerify { repository.update(history) } + confirmVerified(repository) + } } diff --git a/feature/task-history/impl/src/test/java/br/com/sailboat/todozy/feature/task/history/impl/presentation/factory/TaskHistoryUiModelFactoryTest.kt b/feature/task-history/impl/src/test/java/br/com/sailboat/todozy/feature/task/history/impl/presentation/factory/TaskHistoryUiModelFactoryTest.kt index e0b6549f..fcbbc8b9 100644 --- a/feature/task-history/impl/src/test/java/br/com/sailboat/todozy/feature/task/history/impl/presentation/factory/TaskHistoryUiModelFactoryTest.kt +++ b/feature/task-history/impl/src/test/java/br/com/sailboat/todozy/feature/task/history/impl/presentation/factory/TaskHistoryUiModelFactoryTest.kt @@ -24,49 +24,47 @@ internal class TaskHistoryUiModelFactoryTest { ) @Test - fun `should create a subhead and a task history when create is called from taskHistoryUiModelFactory`() = - runBlocking { - val taskHistoryList = - listOf( - TaskHistory( - id = 42L, - taskId = 48L, - taskName = "Task Name", - status = TaskStatus.DONE, - insertingDate = "2022-06-22-19-47-38", - ), - ) - val taskUiModel = - TaskHistoryUiModel( + fun `should create a subhead and a task history when create is called from taskHistoryUiModelFactory`() = runBlocking { + val taskHistoryList = + listOf( + TaskHistory( id = 42L, + taskId = 48L, taskName = "Task Name", - done = true, + status = TaskStatus.DONE, insertingDate = "2022-06-22-19-47-38", - ) - prepareScenario( - taskHistoryListUiModel = listOf(taskUiModel), - subhead = "Today", + ), ) + val taskUiModel = + TaskHistoryUiModel( + id = 42L, + taskName = "Task Name", + done = true, + insertingDate = "2022-06-22-19-47-38", + ) + prepareScenario( + taskHistoryListUiModel = listOf(taskUiModel), + subhead = "Today", + ) - val result = taskHistoryUiModelFactory.create(taskHistoryList, TaskHistoryCategory.TODAY) + val result = taskHistoryUiModelFactory.create(taskHistoryList, TaskHistoryCategory.TODAY) - val expected = - listOf( - SubheadUiModel("Today"), - taskUiModel, - ) - assertEquals(expected, result) - } + val expected = + listOf( + SubheadUiModel("Today"), + taskUiModel, + ) + assertEquals(expected, result) + } @Test - fun `should not create a subhead and a task when task list is empty`() = - runBlocking { - prepareScenario() + fun `should not create a subhead and a task when task list is empty`() = runBlocking { + prepareScenario() - val result = taskHistoryUiModelFactory.create(emptyList(), TaskHistoryCategory.TODAY) + val result = taskHistoryUiModelFactory.create(emptyList(), TaskHistoryCategory.TODAY) - assertEquals(emptyList(), result) - } + assertEquals(emptyList(), result) + } private fun prepareScenario( taskHistoryListUiModel: List = emptyList(), diff --git a/feature/task-history/impl/src/test/java/br/com/sailboat/todozy/feature/task/history/impl/presentation/mapper/TaskHistoryCategoryToStringMapperTest.kt b/feature/task-history/impl/src/test/java/br/com/sailboat/todozy/feature/task/history/impl/presentation/mapper/TaskHistoryCategoryToStringMapperTest.kt index d40f2eca..bb7dcc0f 100644 --- a/feature/task-history/impl/src/test/java/br/com/sailboat/todozy/feature/task/history/impl/presentation/mapper/TaskHistoryCategoryToStringMapperTest.kt +++ b/feature/task-history/impl/src/test/java/br/com/sailboat/todozy/feature/task/history/impl/presentation/mapper/TaskHistoryCategoryToStringMapperTest.kt @@ -17,37 +17,34 @@ internal class TaskHistoryCategoryToStringMapperTest { ) @Test - fun `should map String from task history category TODAY`() = - runBlocking { - val taskHistoryCategoryString = "Today" - prepareScenario(taskHistoryCategoryString = taskHistoryCategoryString) + fun `should map String from task history category TODAY`() = runBlocking { + val taskHistoryCategoryString = "Today" + prepareScenario(taskHistoryCategoryString = taskHistoryCategoryString) - val result = taskHistoryCategoryToStringMapper.map(TaskHistoryCategory.TODAY) + val result = taskHistoryCategoryToStringMapper.map(TaskHistoryCategory.TODAY) - assertEquals(taskHistoryCategoryString, result) - } + assertEquals(taskHistoryCategoryString, result) + } @Test - fun `should map String from task history category YESTERDAY`() = - runBlocking { - val taskHistoryCategoryString = "Yesterday" - prepareScenario(taskHistoryCategoryString = taskHistoryCategoryString) + fun `should map String from task history category YESTERDAY`() = runBlocking { + val taskHistoryCategoryString = "Yesterday" + prepareScenario(taskHistoryCategoryString = taskHistoryCategoryString) - val result = taskHistoryCategoryToStringMapper.map(TaskHistoryCategory.YESTERDAY) + val result = taskHistoryCategoryToStringMapper.map(TaskHistoryCategory.YESTERDAY) - assertEquals(taskHistoryCategoryString, result) - } + assertEquals(taskHistoryCategoryString, result) + } @Test - fun `should map String from task history category PREVIOUS_DAYS`() = - runBlocking { - val taskHistoryCategoryString = "Previous days" - prepareScenario(taskHistoryCategoryString = taskHistoryCategoryString) + fun `should map String from task history category PREVIOUS_DAYS`() = runBlocking { + val taskHistoryCategoryString = "Previous days" + prepareScenario(taskHistoryCategoryString = taskHistoryCategoryString) - val result = taskHistoryCategoryToStringMapper.map(TaskHistoryCategory.PREVIOUS_DAYS) + val result = taskHistoryCategoryToStringMapper.map(TaskHistoryCategory.PREVIOUS_DAYS) - assertEquals(taskHistoryCategoryString, result) - } + assertEquals(taskHistoryCategoryString, result) + } private fun prepareScenario(taskHistoryCategoryString: String = "Today") { coEvery { stringProvider.getString(any()) } returns taskHistoryCategoryString diff --git a/feature/task-history/impl/src/test/java/br/com/sailboat/todozy/feature/task/history/impl/presentation/viewmodel/TaskHistoryViewModelTest.kt b/feature/task-history/impl/src/test/java/br/com/sailboat/todozy/feature/task/history/impl/presentation/viewmodel/TaskHistoryViewModelTest.kt index cb1f2643..5bf80621 100644 --- a/feature/task-history/impl/src/test/java/br/com/sailboat/todozy/feature/task/history/impl/presentation/viewmodel/TaskHistoryViewModelTest.kt +++ b/feature/task-history/impl/src/test/java/br/com/sailboat/todozy/feature/task/history/impl/presentation/viewmodel/TaskHistoryViewModelTest.kt @@ -70,46 +70,44 @@ internal class TaskHistoryViewModelTest { } @Test - fun `should apply a 7-day inclusive range when last seven days filter is selected`() = - runTest(coroutinesTestRule.dispatcher) { - val metricsFilter = slot() - coEvery { getTaskMetricsUseCase(capture(metricsFilter)) } returns Result.success(TaskMetrics(0, 0, 0)) - coEvery { getTaskHistoryUseCase(any()) } answers { Result.success(emptyList()) } - every { taskHistoryUiModelFactory.create(any(), any()) } returns emptyList() + fun `should apply a 7-day inclusive range when last seven days filter is selected`() = runTest(coroutinesTestRule.dispatcher) { + val metricsFilter = slot() + coEvery { getTaskMetricsUseCase(capture(metricsFilter)) } returns Result.success(TaskMetrics(0, 0, 0)) + coEvery { getTaskHistoryUseCase(any()) } answers { Result.success(emptyList()) } + every { taskHistoryUiModelFactory.create(any(), any()) } returns emptyList() - viewModel.dispatchViewIntent( - TaskHistoryViewIntent.OnSelectDateFromFilter(DateFilterTaskHistorySelectableItem.LAST_7_DAYS), - ) - advanceUntilIdle() + viewModel.dispatchViewIntent( + TaskHistoryViewIntent.OnSelectDateFromFilter(DateFilterTaskHistorySelectableItem.LAST_7_DAYS), + ) + advanceUntilIdle() - val today = LocalDate.now() - val expectedInitialDate = TaskProgressRange.LAST_7_DAYS.startDate(today).toStartOfDayCalendar() - val expectedFinalDate = today.toEndOfDayCalendar() - val capturedFilter = metricsFilter.captured + val today = LocalDate.now() + val expectedInitialDate = TaskProgressRange.LAST_7_DAYS.startDate(today).toStartOfDayCalendar() + val expectedFinalDate = today.toEndOfDayCalendar() + val capturedFilter = metricsFilter.captured - assertEquals(expectedInitialDate.timeInMillis, capturedFilter.initialDate?.timeInMillis) - assertEquals(expectedFinalDate.timeInMillis, capturedFilter.finalDate?.timeInMillis) - } + assertEquals(expectedInitialDate.timeInMillis, capturedFilter.initialDate?.timeInMillis) + assertEquals(expectedFinalDate.timeInMillis, capturedFilter.finalDate?.timeInMillis) + } @Test - fun `should apply a 30-day inclusive range when last thirty days filter is selected`() = - runTest(coroutinesTestRule.dispatcher) { - val metricsFilter = slot() - coEvery { getTaskMetricsUseCase(capture(metricsFilter)) } returns Result.success(TaskMetrics(0, 0, 0)) - coEvery { getTaskHistoryUseCase(any()) } answers { Result.success(emptyList()) } - every { taskHistoryUiModelFactory.create(any(), any()) } returns emptyList() + fun `should apply a 30-day inclusive range when last thirty days filter is selected`() = runTest(coroutinesTestRule.dispatcher) { + val metricsFilter = slot() + coEvery { getTaskMetricsUseCase(capture(metricsFilter)) } returns Result.success(TaskMetrics(0, 0, 0)) + coEvery { getTaskHistoryUseCase(any()) } answers { Result.success(emptyList()) } + every { taskHistoryUiModelFactory.create(any(), any()) } returns emptyList() - viewModel.dispatchViewIntent( - TaskHistoryViewIntent.OnSelectDateFromFilter(DateFilterTaskHistorySelectableItem.LAST_30_DAYS), - ) - advanceUntilIdle() + viewModel.dispatchViewIntent( + TaskHistoryViewIntent.OnSelectDateFromFilter(DateFilterTaskHistorySelectableItem.LAST_30_DAYS), + ) + advanceUntilIdle() - val today = LocalDate.now() - val expectedInitialDate = TaskProgressRange.LAST_30_DAYS.startDate(today).toStartOfDayCalendar() - val expectedFinalDate = today.toEndOfDayCalendar() - val capturedFilter = metricsFilter.captured + val today = LocalDate.now() + val expectedInitialDate = TaskProgressRange.LAST_30_DAYS.startDate(today).toStartOfDayCalendar() + val expectedFinalDate = today.toEndOfDayCalendar() + val capturedFilter = metricsFilter.captured - assertEquals(expectedInitialDate.timeInMillis, capturedFilter.initialDate?.timeInMillis) - assertEquals(expectedFinalDate.timeInMillis, capturedFilter.finalDate?.timeInMillis) - } + assertEquals(expectedInitialDate.timeInMillis, capturedFilter.initialDate?.timeInMillis) + assertEquals(expectedFinalDate.timeInMillis, capturedFilter.finalDate?.timeInMillis) + } } diff --git a/feature/task-list/impl/src/main/java/br/com/sailboat/todozy/feature/task/list/impl/data/datasource/TaskLocalDataSourceSQLite.kt b/feature/task-list/impl/src/main/java/br/com/sailboat/todozy/feature/task/list/impl/data/datasource/TaskLocalDataSourceSQLite.kt index 775e4fda..255ce581 100644 --- a/feature/task-list/impl/src/main/java/br/com/sailboat/todozy/feature/task/list/impl/data/datasource/TaskLocalDataSourceSQLite.kt +++ b/feature/task-list/impl/src/main/java/br/com/sailboat/todozy/feature/task/list/impl/data/datasource/TaskLocalDataSourceSQLite.kt @@ -18,133 +18,124 @@ import java.util.Calendar internal class TaskLocalDataSourceSQLite( database: DatabaseOpenHelperService, ) : BaseSQLite(database), TaskLocalDataSource { - override suspend fun getTask(taskId: Long): Result = - runCatching { - val sb = StringBuilder() - bindSelect(sb) - sb.append(" WHERE taskId = $taskId") + override suspend fun getTask(taskId: Long): Result = runCatching { + val sb = StringBuilder() + bindSelect(sb) + sb.append(" WHERE taskId = $taskId") - val cursor = performQuery(sb.toString()) + val cursor = performQuery(sb.toString()) - if (cursor.moveToNext()) { - val task = cursor.mapTask() - cursor.close() - return@runCatching task - } - - throw EntityNotFoundException() + if (cursor.moveToNext()) { + val task = cursor.mapTask() + cursor.close() + return@runCatching task } - override suspend fun getBeforeTodayTasks(filter: TaskFilter) = - runCatching { - val sb = StringBuilder() - bindSelect(sb) - bindDefaultWhere(sb) - bindWhereBeforeToday(sb) - bindWhereFilter(sb, filter) - bindOrderBy(sb) + throw EntityNotFoundException() + } - return@runCatching getListFromQuery(sb.toString(), filter) - } + override suspend fun getBeforeTodayTasks(filter: TaskFilter) = runCatching { + val sb = StringBuilder() + bindSelect(sb) + bindDefaultWhere(sb) + bindWhereBeforeToday(sb) + bindWhereFilter(sb, filter) + bindOrderBy(sb) - override suspend fun getTodayTasks(filter: TaskFilter): Result> = - runCatching { - val sb = StringBuilder() - bindSelect(sb) - bindDefaultWhere(sb) - bindWhereTodayTasks(sb) - bindWhereFilter(sb, filter) - bindOrderBy(sb) + return@runCatching getListFromQuery(sb.toString(), filter) + } - return@runCatching getListFromQuery(sb.toString(), filter) - } - override suspend fun getTomorrowTasks(filter: TaskFilter) = - runCatching { - val sb = StringBuilder() - bindSelect(sb) - bindDefaultWhere(sb) - bindWhereTomorrowTasks(sb) - bindWhereFilter(sb, filter) - bindOrderBy(sb) - - return@runCatching getListFromQuery(sb.toString(), filter) - } + override suspend fun getTodayTasks(filter: TaskFilter): Result> = runCatching { + val sb = StringBuilder() + bindSelect(sb) + bindDefaultWhere(sb) + bindWhereTodayTasks(sb) + bindWhereFilter(sb, filter) + bindOrderBy(sb) - override suspend fun getNextDaysTasks(filter: TaskFilter) = - runCatching { - val sb = StringBuilder() - bindSelect(sb) - bindDefaultWhere(sb) - bindWhereNextDays(sb) - bindWhereFilter(sb, filter) - bindOrderBy(sb) + return@runCatching getListFromQuery(sb.toString(), filter) + } + override suspend fun getTomorrowTasks(filter: TaskFilter) = runCatching { + val sb = StringBuilder() + bindSelect(sb) + bindDefaultWhere(sb) + bindWhereTomorrowTasks(sb) + bindWhereFilter(sb, filter) + bindOrderBy(sb) + + return@runCatching getListFromQuery(sb.toString(), filter) + } - return@runCatching getListFromQuery(sb.toString(), filter) - } + override suspend fun getNextDaysTasks(filter: TaskFilter) = runCatching { + val sb = StringBuilder() + bindSelect(sb) + bindDefaultWhere(sb) + bindWhereNextDays(sb) + bindWhereFilter(sb, filter) + bindOrderBy(sb) - override suspend fun getTasksThrowBeforeNow(): Result> = - runCatching { - val sb = StringBuilder() - bindSelect(sb) - sb.append(" WHERE (Alarm.nextAlarmDate <= '" + parseCalendarToString(Calendar.getInstance()) + "' ") - sb.append(" OR Alarm.nextAlarmDate is null) ") - sb.append(" AND Task.enabled = 1 ") - bindOrderBy(sb) + return@runCatching getListFromQuery(sb.toString(), filter) + } - return@runCatching getListFromQuery(sb.toString(), null) - } + override suspend fun getTasksThrowBeforeNow(): Result> = runCatching { + val sb = StringBuilder() + bindSelect(sb) + sb.append(" WHERE (Alarm.nextAlarmDate <= '" + parseCalendarToString(Calendar.getInstance()) + "' ") + sb.append(" OR Alarm.nextAlarmDate is null) ") + sb.append(" AND Task.enabled = 1 ") + bindOrderBy(sb) - override suspend fun insert(taskData: TaskData): Result = - runCatching { - val sql = StringBuilder() + return@runCatching getListFromQuery(sb.toString(), null) + } - sql.append(" INSERT INTO Task ") - sql.append(" (name, notes, insertingDate, enabled) ") - sql.append(" VALUES (?, ?, ?, ?); ") + override suspend fun insert(taskData: TaskData): Result = runCatching { + val sql = StringBuilder() - val statement = compileStatement(sql.toString()) - statement.bindString(1, taskData.name) - statement.bindString(2, taskData.notes) - statement.bindString(3, parseCalendarToString(Calendar.getInstance())) - statement.bindLong(4, parseBooleanToInt(true).toLong()) + sql.append(" INSERT INTO Task ") + sql.append(" (name, notes, insertingDate, enabled) ") + sql.append(" VALUES (?, ?, ?, ?); ") - return@runCatching insert(statement) - } + val statement = compileStatement(sql.toString()) + statement.bindString(1, taskData.name) + statement.bindString(2, taskData.notes) + statement.bindString(3, parseCalendarToString(Calendar.getInstance())) + statement.bindLong(4, parseBooleanToInt(true).toLong()) + + return@runCatching insert(statement) + } override suspend fun update( taskData: TaskData, enabled: Boolean, - ): Result = - runCatching { - val sql = StringBuilder() - sql.append(" UPDATE Task SET ") - sql.append(" name = ?, ") - sql.append(" notes = ?, ") - sql.append(" insertingDate = ?, ") - sql.append(" enabled = ? ") - sql.append(" WHERE id = ? ") - - val statement = compileStatement(sql.toString()) - statement.bindString(1, taskData.name) - statement.bindString(2, taskData.notes) - statement.bindString(3, getCalendarToBind(Calendar.getInstance())) - statement.bindLong(4, parseBooleanToInt(enabled).toLong()) - statement.bindLong(5, taskData.id) - - update(statement) - } + ): Result = runCatching { + val sql = StringBuilder() + sql.append(" UPDATE Task SET ") + sql.append(" name = ?, ") + sql.append(" notes = ?, ") + sql.append(" insertingDate = ?, ") + sql.append(" enabled = ? ") + sql.append(" WHERE id = ? ") + + val statement = compileStatement(sql.toString()) + statement.bindString(1, taskData.name) + statement.bindString(2, taskData.notes) + statement.bindString(3, getCalendarToBind(Calendar.getInstance())) + statement.bindLong(4, parseBooleanToInt(enabled).toLong()) + statement.bindLong(5, taskData.id) + + update(statement) + } - override suspend fun getTasksWithAlarms(): Result> = - runCatching { - val sb = StringBuilder() - sb.append(" SELECT Task.id as taskId, Task.name as taskName, ") - sb.append(" Task.notes as taskNotes ") - sb.append(" FROM Task INNER JOIN Alarm ") - sb.append(" ON (Task.id = Alarm.fkTaskId) ") - sb.append(" WHERE Task.enabled = 1 ") + override suspend fun getTasksWithAlarms(): Result> = runCatching { + val sb = StringBuilder() + sb.append(" SELECT Task.id as taskId, Task.name as taskName, ") + sb.append(" Task.notes as taskNotes ") + sb.append(" FROM Task INNER JOIN Alarm ") + sb.append(" ON (Task.id = Alarm.fkTaskId) ") + sb.append(" WHERE Task.enabled = 1 ") - return@runCatching getListFromQuery(sb.toString(), null) - } + return@runCatching getListFromQuery(sb.toString(), null) + } private fun getListFromQuery( query: String, @@ -162,13 +153,12 @@ internal class TaskLocalDataSourceSQLite( return tasks } - private fun Cursor.mapTask() = - TaskData( - id = getLong(this, "taskId") ?: Entity.NO_ID, - name = getString(this, "taskName") ?: "", - notes = getString(this, "taskNotes"), - insertingDate = null, - ) + private fun Cursor.mapTask() = TaskData( + id = getLong(this, "taskId") ?: Entity.NO_ID, + name = getString(this, "taskName") ?: "", + notes = getString(this, "taskNotes"), + insertingDate = null, + ) private fun bindSelect(sb: StringBuilder) { sb.append(" SELECT Task.id as taskId, Task.name as taskName, ") diff --git a/feature/task-list/impl/src/main/java/br/com/sailboat/todozy/feature/task/list/impl/data/model/TaskData.kt b/feature/task-list/impl/src/main/java/br/com/sailboat/todozy/feature/task/list/impl/data/model/TaskData.kt index 88efb495..2cf444ca 100644 --- a/feature/task-list/impl/src/main/java/br/com/sailboat/todozy/feature/task/list/impl/data/model/TaskData.kt +++ b/feature/task-list/impl/src/main/java/br/com/sailboat/todozy/feature/task/list/impl/data/model/TaskData.kt @@ -12,17 +12,15 @@ internal data class TaskData( var enabled: Boolean = true, ) -internal fun TaskData.mapToTask(alarm: Alarm?) = - Task( - id = id, - name = name.orEmpty(), - notes = notes, - alarm = alarm, - ) +internal fun TaskData.mapToTask(alarm: Alarm?) = Task( + id = id, + name = name.orEmpty(), + notes = notes, + alarm = alarm, +) -internal fun Task.mapToTaskData() = - TaskData( - id = id, - name = name, - notes = notes, - ) +internal fun Task.mapToTaskData() = TaskData( + id = id, + name = name, + notes = notes, +) diff --git a/feature/task-list/impl/src/main/java/br/com/sailboat/todozy/feature/task/list/impl/data/repository/TaskRepositoryImpl.kt b/feature/task-list/impl/src/main/java/br/com/sailboat/todozy/feature/task/list/impl/data/repository/TaskRepositoryImpl.kt index 3034dc16..7d0d8b6e 100644 --- a/feature/task-list/impl/src/main/java/br/com/sailboat/todozy/feature/task/list/impl/data/repository/TaskRepositoryImpl.kt +++ b/feature/task-list/impl/src/main/java/br/com/sailboat/todozy/feature/task/list/impl/data/repository/TaskRepositoryImpl.kt @@ -8,77 +8,76 @@ import br.com.sailboat.todozy.feature.task.list.impl.data.datasource.TaskLocalDa import br.com.sailboat.todozy.feature.task.list.impl.data.model.TaskData import br.com.sailboat.todozy.feature.task.list.impl.data.model.mapToTask import br.com.sailboat.todozy.feature.task.list.impl.data.model.mapToTaskData +import br.com.sailboat.todozy.utility.kotlin.model.Entity internal class TaskRepositoryImpl( private val alarmRepository: AlarmRepository, private val taskLocalDataSource: TaskLocalDataSource, ) : TaskRepository { - override suspend fun getTask(taskId: Long): Result = - runCatching { - val taskData = taskLocalDataSource.getTask(taskId).getOrThrow() - val alarm = alarmRepository.getAlarmByTaskId(taskData.id).getOrNull() - return@runCatching taskData.mapToTask(alarm) - } + override suspend fun getTask(taskId: Long): Result = runCatching { + val taskData = taskLocalDataSource.getTask(taskId).getOrThrow() + val alarm = alarmRepository.getAlarmByTaskId(taskData.id).getOrNull() + return@runCatching taskData.mapToTask(alarm) + } - override suspend fun getBeforeTodayTasks(filter: TaskFilter): Result> = - runCatching { - val tasksData = taskLocalDataSource.getBeforeTodayTasks(filter).getOrThrow() - return@runCatching tasksData.loadAlarmsAndMapToTasks() - } + override suspend fun getBeforeTodayTasks(filter: TaskFilter): Result> = runCatching { + val tasksData = taskLocalDataSource.getBeforeTodayTasks(filter).getOrThrow() + return@runCatching tasksData.loadAlarmsAndMapToTasks() + } - override suspend fun getTodayTasks(filter: TaskFilter): Result> = - runCatching { - val tasksData = taskLocalDataSource.getTodayTasks(filter).getOrThrow() - return@runCatching tasksData.loadAlarmsAndMapToTasks() - } + override suspend fun getTodayTasks(filter: TaskFilter): Result> = runCatching { + val tasksData = taskLocalDataSource.getTodayTasks(filter).getOrThrow() + return@runCatching tasksData.loadAlarmsAndMapToTasks() + } - override suspend fun getTomorrowTasks(filter: TaskFilter): Result> = - runCatching { - val tasksData = taskLocalDataSource.getTomorrowTasks(filter).getOrThrow() - return@runCatching tasksData.loadAlarmsAndMapToTasks() - } + override suspend fun getTomorrowTasks(filter: TaskFilter): Result> = runCatching { + val tasksData = taskLocalDataSource.getTomorrowTasks(filter).getOrThrow() + return@runCatching tasksData.loadAlarmsAndMapToTasks() + } - override suspend fun getNextDaysTasks(filter: TaskFilter): Result> = - runCatching { - val tasksData = taskLocalDataSource.getNextDaysTasks(filter).getOrThrow() - return@runCatching tasksData.loadAlarmsAndMapToTasks() - } + override suspend fun getNextDaysTasks(filter: TaskFilter): Result> = runCatching { + val tasksData = taskLocalDataSource.getNextDaysTasks(filter).getOrThrow() + return@runCatching tasksData.loadAlarmsAndMapToTasks() + } - override suspend fun getBeforeNowTasks(): Result> = - runCatching { - val tasksData = taskLocalDataSource.getTasksThrowBeforeNow().getOrThrow() - return@runCatching tasksData.loadAlarmsAndMapToTasks() - } + override suspend fun getBeforeNowTasks(): Result> = runCatching { + val tasksData = taskLocalDataSource.getTasksThrowBeforeNow().getOrThrow() + return@runCatching tasksData.loadAlarmsAndMapToTasks() + } - override suspend fun getTasksWithAlarms(): Result> = - runCatching { - val tasksData = taskLocalDataSource.getTasksWithAlarms().getOrThrow() - return@runCatching tasksData.loadAlarmsAndMapToTasks() - } + override suspend fun getTasksWithAlarms(): Result> = runCatching { + val tasksData = taskLocalDataSource.getTasksWithAlarms().getOrThrow() + return@runCatching tasksData.loadAlarmsAndMapToTasks() + } - override suspend fun insert(task: Task): Result = - runCatching { - val taskData = task.mapToTaskData() - val taskId = taskLocalDataSource.insert(taskData).getOrThrow() + override suspend fun insert(task: Task): Result = runCatching { + val taskData = task.mapToTaskData() + val taskId = taskLocalDataSource.insert(taskData).getOrThrow() - return@runCatching task.copy(id = taskId) - } + return@runCatching task.copy(id = taskId) + } - override suspend fun update(task: Task): Result = - runCatching { - taskLocalDataSource.update(task.mapToTaskData(), true) - return@runCatching task - } + override suspend fun update(task: Task): Result = runCatching { + taskLocalDataSource.update(task.mapToTaskData(), true) + return@runCatching task + } - override suspend fun disableTask(task: Task): Result = - runCatching { - taskLocalDataSource.update(task.mapToTaskData(), false) - return@runCatching task + override suspend fun disableTask(task: Task): Result = runCatching { + taskLocalDataSource.update(task.mapToTaskData(), false) + return@runCatching task + } + + private suspend fun List.loadAlarmsAndMapToTasks(): List { + if (isEmpty()) { + return emptyList() } - private suspend fun List.loadAlarmsAndMapToTasks() = - map { taskData -> - val alarm = alarmRepository.getAlarmByTaskId(taskData.id).getOrNull() + val taskIds = mapNotNull { taskData -> taskData.id.takeIf { it != Entity.NO_ID } } + val alarmsByTaskId = alarmRepository.getAlarmsByTaskIds(taskIds).getOrElse { emptyMap() } + + return map { taskData -> + val alarm = alarmsByTaskId[taskData.id] taskData.mapToTask(alarm) } + } } diff --git a/feature/task-list/impl/src/main/java/br/com/sailboat/todozy/feature/task/list/impl/di/TaskListModule.kt b/feature/task-list/impl/src/main/java/br/com/sailboat/todozy/feature/task/list/impl/di/TaskListModule.kt index b5c25266..ae1ba863 100644 --- a/feature/task-list/impl/src/main/java/br/com/sailboat/todozy/feature/task/list/impl/di/TaskListModule.kt +++ b/feature/task-list/impl/src/main/java/br/com/sailboat/todozy/feature/task/list/impl/di/TaskListModule.kt @@ -29,7 +29,6 @@ private val presentation = viewModel { TaskListViewModel( getTasksUseCase = get(), - getAlarmUseCase = get(), scheduleAllAlarmsUseCase = get(), getTaskMetricsUseCase = get(), getTaskProgressUseCase = get(), diff --git a/feature/task-list/impl/src/main/java/br/com/sailboat/todozy/feature/task/list/impl/domain/usecase/CompleteTaskUseCase.kt b/feature/task-list/impl/src/main/java/br/com/sailboat/todozy/feature/task/list/impl/domain/usecase/CompleteTaskUseCase.kt index 46320211..2b54de55 100644 --- a/feature/task-list/impl/src/main/java/br/com/sailboat/todozy/feature/task/list/impl/domain/usecase/CompleteTaskUseCase.kt +++ b/feature/task-list/impl/src/main/java/br/com/sailboat/todozy/feature/task/list/impl/domain/usecase/CompleteTaskUseCase.kt @@ -6,5 +6,5 @@ internal interface CompleteTaskUseCase { suspend operator fun invoke( taskId: Long, status: TaskStatus, - ) + ): Result } diff --git a/feature/task-list/impl/src/main/java/br/com/sailboat/todozy/feature/task/list/impl/domain/usecase/CompleteTaskUseCaseImpl.kt b/feature/task-list/impl/src/main/java/br/com/sailboat/todozy/feature/task/list/impl/domain/usecase/CompleteTaskUseCaseImpl.kt index cd3d4be8..b74bded6 100644 --- a/feature/task-list/impl/src/main/java/br/com/sailboat/todozy/feature/task/list/impl/domain/usecase/CompleteTaskUseCaseImpl.kt +++ b/feature/task-list/impl/src/main/java/br/com/sailboat/todozy/feature/task/list/impl/domain/usecase/CompleteTaskUseCaseImpl.kt @@ -17,14 +17,28 @@ internal class CompleteTaskUseCaseImpl( override suspend operator fun invoke( taskId: Long, status: TaskStatus, - ) { - val task = getTaskUseCase(taskId).getOrThrow() + ): Result { + val task = getTaskUseCase(taskId).getOrElse { throwable -> + return Result.failure(throwable) + } - task.alarm?.takeIf { it.isAlarmRepeating() }?.run { - task.alarm = getNextAlarmUseCase(this) - saveTaskUseCase(task).getOrThrow() - } ?: disableTaskUseCase(task).getOrThrow() + val finalTask = task.alarm?.takeIf { it.isAlarmRepeating() }?.run { + val nextAlarm = getNextAlarmUseCase(this) + val updatedTask = task.copy(alarm = nextAlarm) + saveTaskUseCase(updatedTask).getOrElse { throwable -> + return Result.failure(throwable) + } + updatedTask + } ?: run { + disableTaskUseCase(task).getOrElse { throwable -> + return Result.failure(throwable) + } + } - addHistoryUseCase(task, status).getOrThrow() + addHistoryUseCase(finalTask, status).getOrElse { throwable -> + return Result.failure(throwable) + } + + return Result.success(Unit) } } diff --git a/feature/task-list/impl/src/main/java/br/com/sailboat/todozy/feature/task/list/impl/domain/usecase/GetTasksUseCaseImpl.kt b/feature/task-list/impl/src/main/java/br/com/sailboat/todozy/feature/task/list/impl/domain/usecase/GetTasksUseCaseImpl.kt index 8b37751b..da89395d 100644 --- a/feature/task-list/impl/src/main/java/br/com/sailboat/todozy/feature/task/list/impl/domain/usecase/GetTasksUseCaseImpl.kt +++ b/feature/task-list/impl/src/main/java/br/com/sailboat/todozy/feature/task/list/impl/domain/usecase/GetTasksUseCaseImpl.kt @@ -7,15 +7,14 @@ import br.com.sailboat.todozy.domain.repository.TaskRepository import br.com.sailboat.todozy.feature.task.list.domain.usecase.GetTasksUseCase internal class GetTasksUseCaseImpl(private val taskRepository: TaskRepository) : GetTasksUseCase { - override suspend operator fun invoke(filter: TaskFilter): Result> = - with(taskRepository) { - return when (filter.category) { - TaskCategory.BEFORE_TODAY -> getBeforeTodayTasks(filter) - TaskCategory.BEFORE_NOW -> getBeforeNowTasks() - TaskCategory.TODAY -> getTodayTasks(filter) - TaskCategory.TOMORROW -> getTomorrowTasks(filter) - TaskCategory.NEXT_DAYS -> getNextDaysTasks(filter) - TaskCategory.WITH_ALARMS -> getTasksWithAlarms() - } + override suspend operator fun invoke(filter: TaskFilter): Result> = with(taskRepository) { + return when (filter.category) { + TaskCategory.BEFORE_TODAY -> getBeforeTodayTasks(filter) + TaskCategory.BEFORE_NOW -> getBeforeNowTasks() + TaskCategory.TODAY -> getTodayTasks(filter) + TaskCategory.TOMORROW -> getTomorrowTasks(filter) + TaskCategory.NEXT_DAYS -> getNextDaysTasks(filter) + TaskCategory.WITH_ALARMS -> getTasksWithAlarms() } + } } diff --git a/feature/task-list/impl/src/main/java/br/com/sailboat/todozy/feature/task/list/impl/presentation/TaskListAdapter.kt b/feature/task-list/impl/src/main/java/br/com/sailboat/todozy/feature/task/list/impl/presentation/TaskListAdapter.kt index c03a618b..72c14e3f 100644 --- a/feature/task-list/impl/src/main/java/br/com/sailboat/todozy/feature/task/list/impl/presentation/TaskListAdapter.kt +++ b/feature/task-list/impl/src/main/java/br/com/sailboat/todozy/feature/task/list/impl/presentation/TaskListAdapter.kt @@ -6,8 +6,10 @@ import androidx.recyclerview.widget.RecyclerView import br.com.sailboat.uicomponent.impl.helper.UiModelDiffUtilCallback import br.com.sailboat.uicomponent.impl.viewholder.EmptyViewHolder import br.com.sailboat.uicomponent.impl.viewholder.SubheadViewHolder +import br.com.sailboat.uicomponent.impl.viewholder.TaskSkeletonViewHolder import br.com.sailboat.uicomponent.impl.viewholder.TaskViewHolder import br.com.sailboat.uicomponent.model.SubheadUiModel +import br.com.sailboat.uicomponent.model.TaskSkeletonUiModel import br.com.sailboat.uicomponent.model.TaskUiModel import br.com.sailboat.uicomponent.model.UiModel import br.com.sailboat.uicomponent.model.UiModelType @@ -22,6 +24,7 @@ internal class TaskListAdapter(private val callback: Callback) : ) = when (viewType) { UiModelType.TASK.ordinal -> TaskViewHolder(parent, callback) UiModelType.SUBHEADER.ordinal -> SubheadViewHolder(parent) + UiModelType.TASK_SKELETON.ordinal -> TaskSkeletonViewHolder(parent) else -> EmptyViewHolder(parent) } @@ -32,6 +35,7 @@ internal class TaskListAdapter(private val callback: Callback) : when (val item = getItem(position)) { is TaskUiModel -> (holder as TaskViewHolder).bind(item) is SubheadUiModel -> (holder as SubheadViewHolder).bind(item) + is TaskSkeletonUiModel -> (holder as TaskSkeletonViewHolder).bind(item) } } diff --git a/feature/task-list/impl/src/main/java/br/com/sailboat/todozy/feature/task/list/impl/presentation/TaskListFragment.kt b/feature/task-list/impl/src/main/java/br/com/sailboat/todozy/feature/task/list/impl/presentation/TaskListFragment.kt index aac87ced..709617f8 100644 --- a/feature/task-list/impl/src/main/java/br/com/sailboat/todozy/feature/task/list/impl/presentation/TaskListFragment.kt +++ b/feature/task-list/impl/src/main/java/br/com/sailboat/todozy/feature/task/list/impl/presentation/TaskListFragment.kt @@ -36,6 +36,7 @@ import br.com.sailboat.todozy.utility.android.view.visible import br.com.sailboat.uicomponent.impl.helper.NotificationHelper import br.com.sailboat.uicomponent.impl.helper.SwipeTaskLeftRight import br.com.sailboat.uicomponent.impl.progress.TaskProgressHeaderAdapter +import br.com.sailboat.uicomponent.model.TaskSkeletonUiModel import org.koin.android.ext.android.inject import org.koin.androidx.viewmodel.ext.android.viewModel import br.com.sailboat.todozy.feature.task.list.impl.R as TaskListR @@ -52,6 +53,8 @@ internal class TaskListFragment : Fragment(), SearchMenu by SearchMenuImpl() { private lateinit var binding: FrgTaskListBinding private var taskListAdapter: TaskListAdapter? = null private lateinit var progressAdapter: TaskProgressHeaderAdapter + private var lastTasksLoading = false + private var lastItems: List = emptyList() private val launcher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { @@ -131,28 +134,13 @@ internal class TaskListFragment : Fragment(), SearchMenu by SearchMenuImpl() { private fun observeViewModel() { observeActions() - viewModel.viewState.loading.observe(viewLifecycleOwner) { loading -> - if (loading) { - binding.progressTaskList.visible() - binding.rvTaskList.gone() - } else { - binding.progressTaskList.gone() - binding.rvTaskList.visible() - } + viewModel.viewState.tasksLoading.observe(viewLifecycleOwner) { loading -> + lastTasksLoading = loading + renderList() } viewModel.viewState.itemsView.observe(viewLifecycleOwner) { items -> - taskListAdapter?.submitList(items) - - if (items.isEmpty()) { - binding.rvTaskList.gone() - showEmptyView() - hideMetrics() - val range = viewModel.viewState.taskProgressRange.value ?: TaskProgressRange.LAST_YEAR - progressAdapter.submit(emptyList(), range, isLoading = false) - } else { - binding.rvTaskList.visible() - hideEmptyView() - } + lastItems = items + renderList() } viewModel.viewState.taskMetrics.observe(viewLifecycleOwner) { taskMetrics -> taskMetrics?.run { showMetrics(this) } ?: hideMetrics() @@ -199,6 +187,29 @@ internal class TaskListFragment : Fragment(), SearchMenu by SearchMenuImpl() { binding.eptView.root.visible() } + private fun renderList() { + if (lastTasksLoading) { + hideEmptyView() + hideMetrics() + binding.rvTaskList.visible() + taskListAdapter?.submitList(skeletonItems()) + return + } + + taskListAdapter?.submitList(lastItems) + + if (lastItems.isEmpty()) { + binding.rvTaskList.gone() + showEmptyView() + hideMetrics() + val range = viewModel.viewState.taskProgressRange.value ?: TaskProgressRange.LAST_YEAR + progressAdapter.submit(emptyList(), range, isLoading = false) + } else { + binding.rvTaskList.visible() + hideEmptyView() + } + } + private fun showMetrics(taskMetrics: TaskMetrics) { binding.taskMetrics.tvMetricsFire.text = taskMetrics.consecutiveDone.toString() binding.taskMetrics.tvMetricsDone.text = taskMetrics.doneTasks.toString() @@ -232,7 +243,7 @@ internal class TaskListFragment : Fragment(), SearchMenu by SearchMenuImpl() { } private fun updateRemovedTask(position: Int) { - taskListAdapter?.notifyItemRemoved(position) + // ListAdapter + submitList already handles diff updates; no manual notify needed } private fun showErrorLoadingTasks() { @@ -319,4 +330,13 @@ internal class TaskListFragment : Fragment(), SearchMenu by SearchMenuImpl() { binding.rvTaskList.hideFabWhenScrolling(binding.fab) } + + private fun skeletonItems(): List = + List(SKELETON_ITEMS_COUNT) { index -> TaskSkeletonUiModel(placeholderId = index.toLong()) } + + companion object { + fun newInstance() = TaskListFragment() + + private const val SKELETON_ITEMS_COUNT = 5 + } } diff --git a/feature/task-list/impl/src/main/java/br/com/sailboat/todozy/feature/task/list/impl/presentation/mapper/TaskCategoryToStringMapper.kt b/feature/task-list/impl/src/main/java/br/com/sailboat/todozy/feature/task/list/impl/presentation/mapper/TaskCategoryToStringMapper.kt index ed9aa83b..585784b5 100644 --- a/feature/task-list/impl/src/main/java/br/com/sailboat/todozy/feature/task/list/impl/presentation/mapper/TaskCategoryToStringMapper.kt +++ b/feature/task-list/impl/src/main/java/br/com/sailboat/todozy/feature/task/list/impl/presentation/mapper/TaskCategoryToStringMapper.kt @@ -9,10 +9,11 @@ internal class TaskCategoryToStringMapper(private val stringProvider: StringProv val category = when (taskCategory) { TaskCategory.BEFORE_TODAY -> UiR.string.previous_days + TaskCategory.BEFORE_NOW -> UiR.string.before_now TaskCategory.TODAY -> UiR.string.today TaskCategory.TOMORROW -> UiR.string.tomorrow TaskCategory.NEXT_DAYS -> UiR.string.next_days - else -> UiR.string.empty + TaskCategory.WITH_ALARMS -> UiR.string.with_alarms } return stringProvider.getString(category) } diff --git a/feature/task-list/impl/src/main/java/br/com/sailboat/todozy/feature/task/list/impl/presentation/viewmodel/TaskListViewModel.kt b/feature/task-list/impl/src/main/java/br/com/sailboat/todozy/feature/task/list/impl/presentation/viewmodel/TaskListViewModel.kt index 9051aac2..cacf3d21 100644 --- a/feature/task-list/impl/src/main/java/br/com/sailboat/todozy/feature/task/list/impl/presentation/viewmodel/TaskListViewModel.kt +++ b/feature/task-list/impl/src/main/java/br/com/sailboat/todozy/feature/task/list/impl/presentation/viewmodel/TaskListViewModel.kt @@ -8,7 +8,6 @@ import br.com.sailboat.todozy.domain.model.TaskMetrics import br.com.sailboat.todozy.domain.model.TaskProgressDay import br.com.sailboat.todozy.domain.model.TaskProgressRange import br.com.sailboat.todozy.domain.model.TaskStatus -import br.com.sailboat.todozy.feature.alarm.domain.usecase.GetAlarmUseCase import br.com.sailboat.todozy.feature.alarm.domain.usecase.ScheduleAllAlarmsUseCase import br.com.sailboat.todozy.feature.task.details.domain.usecase.GetTaskMetricsUseCase import br.com.sailboat.todozy.feature.task.history.domain.model.TaskHistoryFilter @@ -50,7 +49,6 @@ private const val TASK_SWIPE_DELAY_IN_MILLIS = 4000L internal class TaskListViewModel( override val viewState: TaskListViewState = TaskListViewState(), private val getTasksUseCase: GetTasksUseCase, - private val getAlarmUseCase: GetAlarmUseCase, private val scheduleAllAlarmsUseCase: ScheduleAllAlarmsUseCase, private val getTaskMetricsUseCase: GetTaskMetricsUseCase, private val getTaskProgressUseCase: GetTaskProgressUseCase, @@ -84,22 +82,23 @@ internal class TaskListViewModel( } } - private fun onStart() = - viewModelScope.launch { - try { - viewState.loading.postValue(true) - viewState.viewAction.postValue(TaskListViewAction.CloseNotifications) - loadTasks() - loadTaskMetrics() - loadProgress() - scheduleAllAlarmsUseCase() - } catch (e: Exception) { - logService.error(e) - viewState.viewAction.value = TaskListViewAction.ShowErrorLoadingTasks - } finally { - viewState.loading.postValue(false) - } + private fun onStart() = viewModelScope.launch { + try { + viewState.tasksLoading.postValue(true) + viewState.taskProgressLoading.postValue(true) + viewState.viewAction.postValue(TaskListViewAction.CloseNotifications) + loadTasks() + viewState.tasksLoading.postValue(false) + loadTaskMetrics() + loadProgress() + scheduleAllAlarmsUseCase() + } catch (e: Exception) { + logService.error(e) + viewState.viewAction.value = TaskListViewAction.ShowErrorLoadingTasks + } finally { + viewState.taskProgressLoading.postValue(false) } + } private fun onClickMenuAbout() { viewState.viewAction.value = TaskListViewAction.NavigateToAbout @@ -121,25 +120,26 @@ internal class TaskListViewModel( viewState.viewAction.value = TaskListViewAction.NavigateToTaskDetails(taskId = taskId) } - private fun onSubmitSearchTerm(term: String) = - viewModelScope.launch { - try { - viewState.loading.postValue(true) - val hasQueryChanged = taskFilter.text != term - taskFilter = taskFilter.copy(text = term) - if (hasQueryChanged) { - clearProgressCache() - } - loadTasks() - loadTaskMetrics() - loadProgress(force = true) - } catch (e: Exception) { - logService.error(e) - viewState.viewAction.value = TaskListViewAction.ShowErrorLoadingTasks - } finally { - viewState.loading.postValue(false) + private fun onSubmitSearchTerm(term: String) = viewModelScope.launch { + try { + viewState.tasksLoading.postValue(true) + viewState.taskProgressLoading.postValue(true) + val hasQueryChanged = taskFilter.text != term + taskFilter = taskFilter.copy(text = term) + if (hasQueryChanged) { + clearProgressCache() } + loadTasks() + viewState.tasksLoading.postValue(false) + loadTaskMetrics() + loadProgress(force = true) + } catch (e: Exception) { + logService.error(e) + viewState.viewAction.value = TaskListViewAction.ShowErrorLoadingTasks + } finally { + viewState.taskProgressLoading.postValue(false) } + } private fun onSelectProgressRange(range: TaskProgressRange) { if (range == selectedProgressRange) { @@ -197,36 +197,38 @@ internal class TaskListViewModel( } } - private suspend fun loadTasks() = - coroutineScope { - val domainTasks = mutableListOf() - val taskCategories = - listOf( - TaskCategory.BEFORE_TODAY, - TaskCategory.TODAY, - TaskCategory.TOMORROW, - TaskCategory.NEXT_DAYS, - ) + private suspend fun loadTasks() = coroutineScope { + val domainTasks = mutableListOf() + val taskCategories = + listOf( + TaskCategory.BEFORE_TODAY, + TaskCategory.TODAY, + TaskCategory.TOMORROW, + TaskCategory.NEXT_DAYS, + ) - val tasks = - taskCategories.map { category -> - async { - val filter = - TaskFilter( - text = taskFilter.text, - category = category, - ) - val tasks = getTasksUseCase(filter).getOrThrow() - - domainTasks.addAll(tasks) - taskListUiModelFactory.create(tasks, category) - } - }.awaitAll().flatten() + val tasks = + taskCategories.map { category -> + async { + val filter = + TaskFilter( + text = taskFilter.text, + category = category, + ) + val tasks = + withContext(dispatcherProvider.io()) { + getTasksUseCase(filter).getOrThrow() + } - val itemsWithFeedback = applyInlineFeedbacks(tasks) - currentTasks = domainTasks - viewState.itemsView.postValue(itemsWithFeedback.toMutableList()) - } + domainTasks.addAll(tasks) + taskListUiModelFactory.create(tasks, category) + } + }.awaitAll().flatten() + + val itemsWithFeedback = applyInlineFeedbacks(tasks) + currentTasks = domainTasks + viewState.itemsView.postValue(itemsWithFeedback.toMutableList()) + } private suspend fun loadTaskMetrics() { runCatching { @@ -335,15 +337,14 @@ internal class TaskListViewModel( } } - private suspend fun loadInlineMetrics(taskId: Long): TaskMetrics? = - runCatching { - withContext(dispatcherProvider.default()) { - val filter = historyFilterForRange(taskId, includeText = true) - getTaskMetricsUseCase(filter).getOrThrow() - } - }.onFailure { throwable -> - logService.error(throwable) - }.getOrNull() + private suspend fun loadInlineMetrics(taskId: Long): TaskMetrics? = runCatching { + withContext(dispatcherProvider.default()) { + val filter = historyFilterForRange(taskId, includeText = true) + getTaskMetricsUseCase(filter).getOrThrow() + } + }.onFailure { throwable -> + logService.error(throwable) + }.getOrNull() private fun publishItemsWithInlineFeedback(baseItems: List? = viewState.itemsView.value) { val updatedItems = applyInlineFeedbacks(baseItems.orEmpty()) @@ -441,7 +442,9 @@ internal class TaskListViewModel( val feedback = inlineTaskFeedbacks[taskId] ?: return runCatching { - completeTaskUseCase(feedback.uiModel.taskId, feedback.status) + withContext(dispatcherProvider.io()) { + completeTaskUseCase(feedback.uiModel.taskId, feedback.status).getOrThrow() + } inlineTaskFeedbacks.remove(taskId) removeTaskFromList(feedback.uiModel.taskId) loadTasks() @@ -557,11 +560,10 @@ internal class TaskListViewModel( progressCache.clear() } - private fun currentProgressCacheKey() = - ProgressCacheKey( - range = selectedProgressRange, - searchTerm = taskFilter.text.orEmpty(), - ) + private fun currentProgressCacheKey() = ProgressCacheKey( + range = selectedProgressRange, + searchTerm = taskFilter.text.orEmpty(), + ) private fun updateProgressDays( days: List, diff --git a/feature/task-list/impl/src/main/java/br/com/sailboat/todozy/feature/task/list/impl/presentation/viewmodel/TaskListViewState.kt b/feature/task-list/impl/src/main/java/br/com/sailboat/todozy/feature/task/list/impl/presentation/viewmodel/TaskListViewState.kt index 9d0d7120..e918c1fb 100644 --- a/feature/task-list/impl/src/main/java/br/com/sailboat/todozy/feature/task/list/impl/presentation/viewmodel/TaskListViewState.kt +++ b/feature/task-list/impl/src/main/java/br/com/sailboat/todozy/feature/task/list/impl/presentation/viewmodel/TaskListViewState.kt @@ -9,7 +9,7 @@ import br.com.sailboat.uicomponent.model.UiModel internal class TaskListViewState { val viewAction = Event() - val loading = MutableLiveData(true) + val tasksLoading = MutableLiveData(false) val itemsView = MutableLiveData>() val taskMetrics = MutableLiveData() val taskProgressDays = MutableLiveData>(emptyList()) diff --git a/feature/task-list/impl/src/test/java/br/com/sailboat/todozy/feature/task/list/impl/data/TaskRepositoryImplTest.kt b/feature/task-list/impl/src/test/java/br/com/sailboat/todozy/feature/task/list/impl/data/TaskRepositoryImplTest.kt index 6cbeb28f..982f10bf 100644 --- a/feature/task-list/impl/src/test/java/br/com/sailboat/todozy/feature/task/list/impl/data/TaskRepositoryImplTest.kt +++ b/feature/task-list/impl/src/test/java/br/com/sailboat/todozy/feature/task/list/impl/data/TaskRepositoryImplTest.kt @@ -28,258 +28,248 @@ internal class TaskRepositoryImplTest { ) @Test - fun `should call getTask from taskLocalDataSource when getTask is called from taskRepository`() = - runBlocking { - val taskId = 45L - val taskData = TaskDataMockFactory.makeTaskData() - val alarm = AlarmMockFactory.makeAlarm() - prepareScenario( - taskDataResult = Result.success(taskData), - alarmResult = Result.success(alarm), + fun `should call getTask from taskLocalDataSource when getTask is called from taskRepository`() = runBlocking { + val taskId = 45L + val taskData = TaskDataMockFactory.makeTaskData() + val alarm = AlarmMockFactory.makeAlarm() + prepareScenario( + taskDataResult = Result.success(taskData), + alarmResult = Result.success(alarm), + ) + + val result = taskRepository.getTask(taskId) + + val expected = + Result.success( + Task( + id = taskData.id, + name = taskData.name.orEmpty(), + notes = taskData.notes, + alarm = alarm, + ), ) + assertEquals(expected, result) + coVerify { taskLocalDataSource.getTask(taskId) } + } - val result = taskRepository.getTask(taskId) + @Test + fun `should call getBeforeTodayTasks from taskLocalDataSource when getBeforeTodayTasks is called from taskRepository`() = runBlocking { + val taskDataResult = TaskDataMockFactory.makeTaskData() + val taskFilter = TaskFilter(category = TaskCategory.BEFORE_TODAY) + val alarm = AlarmMockFactory.makeAlarm() + prepareScenario( + taskDataListResult = Result.success(listOf(taskDataResult)), + alarmResult = Result.success(alarm), + ) - val expected = - Result.success( + val result = taskRepository.getBeforeTodayTasks(taskFilter) + + val expected = + Result.success( + listOf( Task( - id = taskData.id, - name = taskData.name.orEmpty(), - notes = taskData.notes, + id = taskDataResult.id, + name = taskDataResult.name.orEmpty(), + notes = taskDataResult.notes, alarm = alarm, ), - ) - assertEquals(expected, result) - coVerify { taskLocalDataSource.getTask(taskId) } - } + ), + ) + assertEquals(expected, result) + coVerify { taskLocalDataSource.getBeforeTodayTasks(taskFilter) } + } @Test - fun `should call getBeforeTodayTasks from taskLocalDataSource when getBeforeTodayTasks is called from taskRepository`() = - runBlocking { - val taskDataResult = TaskDataMockFactory.makeTaskData() - val taskFilter = TaskFilter(category = TaskCategory.BEFORE_TODAY) - val alarm = AlarmMockFactory.makeAlarm() - prepareScenario( - taskDataListResult = Result.success(listOf(taskDataResult)), - alarmResult = Result.success(alarm), - ) + fun `should call getTodayTasks from taskLocalDataSource when getTodayTasks is called from taskRepository`() = runBlocking { + val taskDataResult = TaskDataMockFactory.makeTaskData() + val taskFilter = TaskFilter(category = TaskCategory.TODAY) + val alarm = AlarmMockFactory.makeAlarm() + prepareScenario( + taskDataListResult = Result.success(listOf(taskDataResult)), + alarmResult = Result.success(alarm), + ) - val result = taskRepository.getBeforeTodayTasks(taskFilter) - - val expected = - Result.success( - listOf( - Task( - id = taskDataResult.id, - name = taskDataResult.name.orEmpty(), - notes = taskDataResult.notes, - alarm = alarm, - ), + val result = taskRepository.getTodayTasks(taskFilter) + + val expected = + Result.success( + listOf( + Task( + id = taskDataResult.id, + name = taskDataResult.name.orEmpty(), + notes = taskDataResult.notes, + alarm = alarm, ), - ) - assertEquals(expected, result) - coVerify { taskLocalDataSource.getBeforeTodayTasks(taskFilter) } - } + ), + ) + assertEquals(expected, result) + coVerify { taskLocalDataSource.getTodayTasks(taskFilter) } + } @Test - fun `should call getTodayTasks from taskLocalDataSource when getTodayTasks is called from taskRepository`() = - runBlocking { - val taskDataResult = TaskDataMockFactory.makeTaskData() - val taskFilter = TaskFilter(category = TaskCategory.TODAY) - val alarm = AlarmMockFactory.makeAlarm() - prepareScenario( - taskDataListResult = Result.success(listOf(taskDataResult)), - alarmResult = Result.success(alarm), - ) + fun `should call getTomorrowTasks from taskLocalDataSource when getTomorrowTasks is called from taskRepository`() = runBlocking { + val taskDataResult = TaskDataMockFactory.makeTaskData() + val taskFilter = TaskFilter(category = TaskCategory.TOMORROW) + val alarm = AlarmMockFactory.makeAlarm() + prepareScenario( + taskDataListResult = Result.success(listOf(taskDataResult)), + alarmResult = Result.success(alarm), + ) - val result = taskRepository.getTodayTasks(taskFilter) - - val expected = - Result.success( - listOf( - Task( - id = taskDataResult.id, - name = taskDataResult.name.orEmpty(), - notes = taskDataResult.notes, - alarm = alarm, - ), + val result = taskRepository.getTomorrowTasks(taskFilter) + + val expected = + Result.success( + listOf( + Task( + id = taskDataResult.id, + name = taskDataResult.name.orEmpty(), + notes = taskDataResult.notes, + alarm = alarm, ), - ) - assertEquals(expected, result) - coVerify { taskLocalDataSource.getTodayTasks(taskFilter) } - } + ), + ) + assertEquals(expected, result) + coVerify { taskLocalDataSource.getTomorrowTasks(taskFilter) } + } @Test - fun `should call getTomorrowTasks from taskLocalDataSource when getTomorrowTasks is called from taskRepository`() = - runBlocking { - val taskDataResult = TaskDataMockFactory.makeTaskData() - val taskFilter = TaskFilter(category = TaskCategory.TOMORROW) - val alarm = AlarmMockFactory.makeAlarm() - prepareScenario( - taskDataListResult = Result.success(listOf(taskDataResult)), - alarmResult = Result.success(alarm), - ) + fun `should call getNextDaysTasks from taskLocalDataSource when getNextDaysTasks is called from taskRepository`() = runBlocking { + val taskDataResult = TaskDataMockFactory.makeTaskData() + val taskFilter = TaskFilter(category = TaskCategory.TOMORROW) + val alarm = AlarmMockFactory.makeAlarm() + prepareScenario( + taskDataListResult = Result.success(listOf(taskDataResult)), + alarmResult = Result.success(alarm), + ) - val result = taskRepository.getTomorrowTasks(taskFilter) - - val expected = - Result.success( - listOf( - Task( - id = taskDataResult.id, - name = taskDataResult.name.orEmpty(), - notes = taskDataResult.notes, - alarm = alarm, - ), + val result = taskRepository.getNextDaysTasks(taskFilter) + + val expected = + Result.success( + listOf( + Task( + id = taskDataResult.id, + name = taskDataResult.name.orEmpty(), + notes = taskDataResult.notes, + alarm = alarm, ), - ) - assertEquals(expected, result) - coVerify { taskLocalDataSource.getTomorrowTasks(taskFilter) } - } + ), + ) + assertEquals(expected, result) + coVerify { taskLocalDataSource.getNextDaysTasks(taskFilter) } + } @Test - fun `should call getNextDaysTasks from taskLocalDataSource when getNextDaysTasks is called from taskRepository`() = - runBlocking { - val taskDataResult = TaskDataMockFactory.makeTaskData() - val taskFilter = TaskFilter(category = TaskCategory.TOMORROW) - val alarm = AlarmMockFactory.makeAlarm() - prepareScenario( - taskDataListResult = Result.success(listOf(taskDataResult)), - alarmResult = Result.success(alarm), - ) + fun `should call getTasksThrowBeforeNow from taskLocalDataSource when getBeforeNowTasks is called from taskRepository`() = runBlocking { + val taskDataResult = TaskDataMockFactory.makeTaskData() + val alarm = AlarmMockFactory.makeAlarm() + prepareScenario( + taskDataListResult = Result.success(listOf(taskDataResult)), + alarmResult = Result.success(alarm), + ) - val result = taskRepository.getNextDaysTasks(taskFilter) - - val expected = - Result.success( - listOf( - Task( - id = taskDataResult.id, - name = taskDataResult.name.orEmpty(), - notes = taskDataResult.notes, - alarm = alarm, - ), + val result = taskRepository.getBeforeNowTasks() + + val expected = + Result.success( + listOf( + Task( + id = taskDataResult.id, + name = taskDataResult.name.orEmpty(), + notes = taskDataResult.notes, + alarm = alarm, ), - ) - assertEquals(expected, result) - coVerify { taskLocalDataSource.getNextDaysTasks(taskFilter) } - } + ), + ) + assertEquals(expected, result) + coVerify { taskLocalDataSource.getTasksThrowBeforeNow() } + } @Test - fun `should call getTasksThrowBeforeNow from taskLocalDataSource when getBeforeNowTasks is called from taskRepository`() = - runBlocking { - val taskDataResult = TaskDataMockFactory.makeTaskData() - val alarm = AlarmMockFactory.makeAlarm() - prepareScenario( - taskDataListResult = Result.success(listOf(taskDataResult)), - alarmResult = Result.success(alarm), - ) + fun `should call getTasksWithAlarms from taskLocalDataSource when getTasksWithAlarms is called from taskRepository`() = runBlocking { + val taskDataResult = TaskDataMockFactory.makeTaskData() + val alarm = AlarmMockFactory.makeAlarm() + prepareScenario( + taskDataListResult = Result.success(listOf(taskDataResult)), + alarmResult = Result.success(alarm), + ) - val result = taskRepository.getBeforeNowTasks() - - val expected = - Result.success( - listOf( - Task( - id = taskDataResult.id, - name = taskDataResult.name.orEmpty(), - notes = taskDataResult.notes, - alarm = alarm, - ), + val result = taskRepository.getTasksWithAlarms() + + val expected = + Result.success( + listOf( + Task( + id = taskDataResult.id, + name = taskDataResult.name.orEmpty(), + notes = taskDataResult.notes, + alarm = alarm, ), - ) - assertEquals(expected, result) - coVerify { taskLocalDataSource.getTasksThrowBeforeNow() } - } + ), + ) + assertEquals(expected, result) + coVerify { taskLocalDataSource.getTasksWithAlarms() } + } @Test - fun `should call getTasksWithAlarms from taskLocalDataSource when getTasksWithAlarms is called from taskRepository`() = - runBlocking { - val taskDataResult = TaskDataMockFactory.makeTaskData() - val alarm = AlarmMockFactory.makeAlarm() - prepareScenario( - taskDataListResult = Result.success(listOf(taskDataResult)), - alarmResult = Result.success(alarm), + fun `should call insert from taskLocalDataSource when insert is called from taskRepository`() = runBlocking { + val task = TaskMockFactory.makeTask() + val taskId = 45L + prepareScenario(insertResult = Result.success(taskId)) + + taskRepository.insert(task) + + coVerify { + taskLocalDataSource.insert( + TaskData( + id = taskId, + name = task.name, + notes = task.notes, + ), ) - - val result = taskRepository.getTasksWithAlarms() - - val expected = - Result.success( - listOf( - Task( - id = taskDataResult.id, - name = taskDataResult.name.orEmpty(), - notes = taskDataResult.notes, - alarm = alarm, - ), - ), - ) - assertEquals(expected, result) - coVerify { taskLocalDataSource.getTasksWithAlarms() } } + } @Test - fun `should call insert from taskLocalDataSource when insert is called from taskRepository`() = - runBlocking { - val task = TaskMockFactory.makeTask() - val taskId = 45L - prepareScenario(insertResult = Result.success(taskId)) + fun `should call update from taskLocalDataSource when update is called from taskRepository`() = runBlocking { + val task = TaskMockFactory.makeTask() + prepareScenario() - taskRepository.insert(task) + taskRepository.update(task) - coVerify { - taskLocalDataSource.insert( + coVerify { + taskLocalDataSource.update( + taskData = TaskData( - id = taskId, + id = task.id, name = task.name, notes = task.notes, ), - ) - } + enabled = true, + ) } + } @Test - fun `should call update from taskLocalDataSource when update is called from taskRepository`() = - runBlocking { - val task = TaskMockFactory.makeTask() - prepareScenario() - - taskRepository.update(task) - - coVerify { - taskLocalDataSource.update( - taskData = - TaskData( - id = task.id, - name = task.name, - notes = task.notes, - ), - enabled = true, - ) - } - } + fun `should call update from taskLocalDataSource with enabled false when disableTask is called from taskRepository`() = runBlocking { + val task = TaskMockFactory.makeTask() + prepareScenario() - @Test - fun `should call update from taskLocalDataSource with enabled false when disableTask is called from taskRepository`() = - runBlocking { - val task = TaskMockFactory.makeTask() - prepareScenario() - - taskRepository.disableTask(task) - - coVerify { - taskLocalDataSource.update( - taskData = - TaskData( - id = task.id, - name = task.name, - notes = task.notes, - ), - enabled = false, - ) - } + taskRepository.disableTask(task) + + coVerify { + taskLocalDataSource.update( + taskData = + TaskData( + id = task.id, + name = task.name, + notes = task.notes, + ), + enabled = false, + ) } + } private fun prepareScenario( taskDataResult: Result = Result.success(TaskDataMockFactory.makeTaskData()), @@ -288,6 +278,12 @@ internal class TaskRepositoryImplTest { TaskDataMockFactory.makeTaskDataList(), ), alarmResult: Result = Result.success(AlarmMockFactory.makeAlarm()), + alarmMapResult: Result> = + if (alarmResult.isSuccess) { + alarmResult.getOrNull()?.let { Result.success(mapOf(45L to it)) } ?: Result.success(emptyMap()) + } else { + Result.success(emptyMap()) + }, insertResult: Result = Result.success(42L), updateResult: Result = Result.success(Unit), ) { @@ -301,5 +297,6 @@ internal class TaskRepositoryImplTest { coEvery { taskLocalDataSource.insert(any()) } returns insertResult coEvery { taskLocalDataSource.update(any(), any()) } returns updateResult coEvery { alarmRepository.getAlarmByTaskId(any()) } returns alarmResult + coEvery { alarmRepository.getAlarmsByTaskIds(any()) } returns alarmMapResult } } diff --git a/feature/task-list/impl/src/test/java/br/com/sailboat/todozy/feature/task/list/impl/domain/usecase/CompleteTaskUseCaseImplTest.kt b/feature/task-list/impl/src/test/java/br/com/sailboat/todozy/feature/task/list/impl/domain/usecase/CompleteTaskUseCaseImplTest.kt index a457cadb..41e9f2af 100644 --- a/feature/task-list/impl/src/test/java/br/com/sailboat/todozy/feature/task/list/impl/domain/usecase/CompleteTaskUseCaseImplTest.kt +++ b/feature/task-list/impl/src/test/java/br/com/sailboat/todozy/feature/task/list/impl/domain/usecase/CompleteTaskUseCaseImplTest.kt @@ -16,6 +16,7 @@ import io.mockk.mockk import kotlinx.coroutines.runBlocking import org.junit.Test import java.util.Calendar +import kotlin.test.assertTrue internal class CompleteTaskUseCaseImplTest { private val getTaskUseCase: GetTaskUseCase = mockk(relaxed = true) @@ -34,104 +35,104 @@ internal class CompleteTaskUseCaseImplTest { ) @Test - fun `should get task from id`() = - runBlocking { - val taskId = 42L - val task = - Task( - id = taskId, - name = "Task Name", - notes = "Some notes", - ) - prepareScenario(taskResult = Result.success(task)) + fun `should get task from id`() = runBlocking { + val taskId = 42L + val task = + Task( + id = taskId, + name = "Task Name", + notes = "Some notes", + ) + prepareScenario(taskResult = Result.success(task)) - completeTaskUseCase(taskId, TaskStatus.DONE) + val result = completeTaskUseCase(taskId, TaskStatus.DONE) - coVerify { getTaskUseCase(taskId) } - confirmVerified(getTaskUseCase) - } + assertTrue(result.isSuccess) + coVerify { getTaskUseCase(taskId) } + confirmVerified(getTaskUseCase) + } @Test - fun `should disable task when alarm is non-repetitive`() = - runBlocking { - val taskId = 42L - val task = - Task( - id = taskId, - name = "Task Name", - notes = "Some notes", - ) - prepareScenario(taskResult = Result.success(task)) + fun `should disable task when alarm is non-repetitive`() = runBlocking { + val taskId = 42L + val task = + Task( + id = taskId, + name = "Task Name", + notes = "Some notes", + ) + prepareScenario(taskResult = Result.success(task)) - completeTaskUseCase(taskId, TaskStatus.DONE) + val result = completeTaskUseCase(taskId, TaskStatus.DONE) - coVerify { disableTaskUseCase(task) } - confirmVerified(disableTaskUseCase) - } + assertTrue(result.isSuccess) + coVerify { disableTaskUseCase(task) } + confirmVerified(disableTaskUseCase) + } @Test - fun `should disable task when alarm is null`() = - runBlocking { - val taskId = 42L - val task = - Task( - id = taskId, - name = "Task Name", - notes = "Some notes", - ) - prepareScenario(taskResult = Result.success(task)) + fun `should disable task when alarm is null`() = runBlocking { + val taskId = 42L + val task = + Task( + id = taskId, + name = "Task Name", + notes = "Some notes", + ) + prepareScenario(taskResult = Result.success(task)) - completeTaskUseCase(taskId, TaskStatus.DONE) + val result = completeTaskUseCase(taskId, TaskStatus.DONE) - coVerify { disableTaskUseCase(task) } - confirmVerified(disableTaskUseCase) - } + assertTrue(result.isSuccess) + coVerify { disableTaskUseCase(task) } + confirmVerified(disableTaskUseCase) + } @Test - fun `should get next alarm and save task when alarm is repetitive`() = - runBlocking { - val alarm = - Alarm( - dateTime = Calendar.getInstance(), - repeatType = RepeatType.DAY, - ) - val taskId = 42L - val task = - Task( - id = taskId, - name = "Task Name", - notes = "Some notes", - alarm = alarm, - ) - prepareScenario( - taskResult = Result.success(task), - alarmResult = alarm, + fun `should get next alarm and save task when alarm is repetitive`() = runBlocking { + val alarm = + Alarm( + dateTime = Calendar.getInstance(), + repeatType = RepeatType.DAY, ) + val taskId = 42L + val task = + Task( + id = taskId, + name = "Task Name", + notes = "Some notes", + alarm = alarm, + ) + prepareScenario( + taskResult = Result.success(task), + alarmResult = alarm, + ) - completeTaskUseCase(taskId, TaskStatus.DONE) + val result = completeTaskUseCase(taskId, TaskStatus.DONE) - coVerify { getNextAlarmUseCase(alarm) } - coVerify { saveTaskUseCase(task) } - confirmVerified(saveTaskUseCase) - confirmVerified(getNextAlarmUseCase) - } + assertTrue(result.isSuccess) + coVerify { getNextAlarmUseCase(alarm) } + coVerify { saveTaskUseCase(task) } + confirmVerified(saveTaskUseCase) + confirmVerified(getNextAlarmUseCase) + } @Test - fun `should add history after complete task`() = - runBlocking { - val task = - Task( - id = 42L, - name = "Task Name", - notes = "Some notes", - ) - prepareScenario(taskResult = Result.success(task)) + fun `should add history after complete task`() = runBlocking { + val task = + Task( + id = 42L, + name = "Task Name", + notes = "Some notes", + ) + prepareScenario(taskResult = Result.success(task)) - completeTaskUseCase(45, TaskStatus.DONE) + val result = completeTaskUseCase(45, TaskStatus.DONE) - coVerify { addHistoryUseCase(task, TaskStatus.DONE) } - confirmVerified(addHistoryUseCase) - } + assertTrue(result.isSuccess) + coVerify { addHistoryUseCase(task, TaskStatus.DONE) } + confirmVerified(addHistoryUseCase) + } private fun prepareScenario( taskResult: Result = @@ -152,5 +153,6 @@ internal class CompleteTaskUseCaseImplTest { coEvery { getNextAlarmUseCase(any()) } returns alarmResult coEvery { disableTaskUseCase(any()) } returns taskResult coEvery { saveTaskUseCase(any()) } returns taskResult + coEvery { addHistoryUseCase(any(), any()) } returns Result.success(Unit) } } diff --git a/feature/task-list/impl/src/test/java/br/com/sailboat/todozy/feature/task/list/impl/domain/usecase/GetTasksUseCaseImplTest.kt b/feature/task-list/impl/src/test/java/br/com/sailboat/todozy/feature/task/list/impl/domain/usecase/GetTasksUseCaseImplTest.kt index a101f059..e727787c 100644 --- a/feature/task-list/impl/src/test/java/br/com/sailboat/todozy/feature/task/list/impl/domain/usecase/GetTasksUseCaseImplTest.kt +++ b/feature/task-list/impl/src/test/java/br/com/sailboat/todozy/feature/task/list/impl/domain/usecase/GetTasksUseCaseImplTest.kt @@ -18,89 +18,84 @@ internal class GetTasksUseCaseImplTest { private val getTasksUseCase = GetTasksUseCaseImpl(repository) @Test - fun `should get tasks from repository with alarms triggered before now`() = - runBlocking { - val tasksResult = Result.success(TaskMockFactory.makeTaskList()) - prepareScenario(tasksResult = tasksResult) - - val result = getTasksUseCase(TaskFilter(category = TaskCategory.BEFORE_NOW)) - - coVerify(exactly = 1) { repository.getBeforeNowTasks() } - coVerify(exactly = 0) { repository.getBeforeTodayTasks(any()) } - coVerify(exactly = 0) { repository.getTodayTasks(any()) } - coVerify(exactly = 0) { repository.getTomorrowTasks(any()) } - coVerify(exactly = 0) { repository.getNextDaysTasks(any()) } - confirmVerified(repository) - assertEquals(tasksResult, result) - } + fun `should get tasks from repository with alarms triggered before now`() = runBlocking { + val tasksResult = Result.success(TaskMockFactory.makeTaskList()) + prepareScenario(tasksResult = tasksResult) + + val result = getTasksUseCase(TaskFilter(category = TaskCategory.BEFORE_NOW)) + + coVerify(exactly = 1) { repository.getBeforeNowTasks() } + coVerify(exactly = 0) { repository.getBeforeTodayTasks(any()) } + coVerify(exactly = 0) { repository.getTodayTasks(any()) } + coVerify(exactly = 0) { repository.getTomorrowTasks(any()) } + coVerify(exactly = 0) { repository.getNextDaysTasks(any()) } + confirmVerified(repository) + assertEquals(tasksResult, result) + } @Test - fun `should get tasks from repository with alarms triggered before today`() = - runBlocking { - val tasksResult = Result.success(TaskMockFactory.makeTaskList()) - prepareScenario(tasksResult = tasksResult) - - val result = getTasksUseCase(TaskFilter(category = TaskCategory.BEFORE_TODAY)) - - coVerify(exactly = 0) { repository.getBeforeNowTasks() } - coVerify(exactly = 1) { repository.getBeforeTodayTasks(any()) } - coVerify(exactly = 0) { repository.getTodayTasks(any()) } - coVerify(exactly = 0) { repository.getTomorrowTasks(any()) } - coVerify(exactly = 0) { repository.getNextDaysTasks(any()) } - confirmVerified(repository) - assertEquals(tasksResult, result) - } + fun `should get tasks from repository with alarms triggered before today`() = runBlocking { + val tasksResult = Result.success(TaskMockFactory.makeTaskList()) + prepareScenario(tasksResult = tasksResult) + + val result = getTasksUseCase(TaskFilter(category = TaskCategory.BEFORE_TODAY)) + + coVerify(exactly = 0) { repository.getBeforeNowTasks() } + coVerify(exactly = 1) { repository.getBeforeTodayTasks(any()) } + coVerify(exactly = 0) { repository.getTodayTasks(any()) } + coVerify(exactly = 0) { repository.getTomorrowTasks(any()) } + coVerify(exactly = 0) { repository.getNextDaysTasks(any()) } + confirmVerified(repository) + assertEquals(tasksResult, result) + } @Test - fun `should get tasks from repository with alarms for today or without any alarm`() = - runBlocking { - val tasksResult = Result.success(TaskMockFactory.makeTaskList()) - prepareScenario(tasksResult = tasksResult) - - val result = getTasksUseCase(TaskFilter(category = TaskCategory.TODAY)) - - coVerify(exactly = 0) { repository.getBeforeNowTasks() } - coVerify(exactly = 0) { repository.getBeforeTodayTasks(any()) } - coVerify(exactly = 1) { repository.getTodayTasks(any()) } - coVerify(exactly = 0) { repository.getTomorrowTasks(any()) } - coVerify(exactly = 0) { repository.getNextDaysTasks(any()) } - confirmVerified(repository) - assertEquals(tasksResult, result) - } + fun `should get tasks from repository with alarms for today or without any alarm`() = runBlocking { + val tasksResult = Result.success(TaskMockFactory.makeTaskList()) + prepareScenario(tasksResult = tasksResult) + + val result = getTasksUseCase(TaskFilter(category = TaskCategory.TODAY)) + + coVerify(exactly = 0) { repository.getBeforeNowTasks() } + coVerify(exactly = 0) { repository.getBeforeTodayTasks(any()) } + coVerify(exactly = 1) { repository.getTodayTasks(any()) } + coVerify(exactly = 0) { repository.getTomorrowTasks(any()) } + coVerify(exactly = 0) { repository.getNextDaysTasks(any()) } + confirmVerified(repository) + assertEquals(tasksResult, result) + } @Test - fun `should get tasks from repository with alarms for tomorrow`() = - runBlocking { - val tasksResult = Result.success(TaskMockFactory.makeTaskList()) - prepareScenario(tasksResult = tasksResult) - - val result = getTasksUseCase(TaskFilter(category = TaskCategory.TOMORROW)) - - coVerify(exactly = 0) { repository.getBeforeNowTasks() } - coVerify(exactly = 0) { repository.getBeforeTodayTasks(any()) } - coVerify(exactly = 0) { repository.getTodayTasks(any()) } - coVerify(exactly = 1) { repository.getTomorrowTasks(any()) } - coVerify(exactly = 0) { repository.getNextDaysTasks(any()) } - confirmVerified(repository) - assertEquals(tasksResult, result) - } + fun `should get tasks from repository with alarms for tomorrow`() = runBlocking { + val tasksResult = Result.success(TaskMockFactory.makeTaskList()) + prepareScenario(tasksResult = tasksResult) + + val result = getTasksUseCase(TaskFilter(category = TaskCategory.TOMORROW)) + + coVerify(exactly = 0) { repository.getBeforeNowTasks() } + coVerify(exactly = 0) { repository.getBeforeTodayTasks(any()) } + coVerify(exactly = 0) { repository.getTodayTasks(any()) } + coVerify(exactly = 1) { repository.getTomorrowTasks(any()) } + coVerify(exactly = 0) { repository.getNextDaysTasks(any()) } + confirmVerified(repository) + assertEquals(tasksResult, result) + } @Test - fun `should get tasks from repository with alarms for next days`() = - runBlocking { - val tasksResult = Result.success(TaskMockFactory.makeTaskList()) - prepareScenario(tasksResult = tasksResult) - - val result = getTasksUseCase(TaskFilter(category = TaskCategory.NEXT_DAYS)) - - coVerify(exactly = 0) { repository.getBeforeNowTasks() } - coVerify(exactly = 0) { repository.getBeforeTodayTasks(any()) } - coVerify(exactly = 0) { repository.getTodayTasks(any()) } - coVerify(exactly = 0) { repository.getTomorrowTasks(any()) } - coVerify(exactly = 1) { repository.getNextDaysTasks(any()) } - confirmVerified(repository) - assertEquals(tasksResult, result) - } + fun `should get tasks from repository with alarms for next days`() = runBlocking { + val tasksResult = Result.success(TaskMockFactory.makeTaskList()) + prepareScenario(tasksResult = tasksResult) + + val result = getTasksUseCase(TaskFilter(category = TaskCategory.NEXT_DAYS)) + + coVerify(exactly = 0) { repository.getBeforeNowTasks() } + coVerify(exactly = 0) { repository.getBeforeTodayTasks(any()) } + coVerify(exactly = 0) { repository.getTodayTasks(any()) } + coVerify(exactly = 0) { repository.getTomorrowTasks(any()) } + coVerify(exactly = 1) { repository.getNextDaysTasks(any()) } + confirmVerified(repository) + assertEquals(tasksResult, result) + } private fun prepareScenario(tasksResult: Result> = Result.success(TaskMockFactory.makeTaskList())) { coEvery { repository.getBeforeNowTasks() } returns tasksResult diff --git a/feature/task-list/impl/src/test/java/br/com/sailboat/todozy/feature/task/list/impl/presentation/factory/TaskListUiModelFactoryTest.kt b/feature/task-list/impl/src/test/java/br/com/sailboat/todozy/feature/task/list/impl/presentation/factory/TaskListUiModelFactoryTest.kt index 122ed3a3..3595d695 100644 --- a/feature/task-list/impl/src/test/java/br/com/sailboat/todozy/feature/task/list/impl/presentation/factory/TaskListUiModelFactoryTest.kt +++ b/feature/task-list/impl/src/test/java/br/com/sailboat/todozy/feature/task/list/impl/presentation/factory/TaskListUiModelFactoryTest.kt @@ -23,45 +23,43 @@ internal class TaskListUiModelFactoryTest { ) @Test - fun `should create a subhead and a task when create is called from taskListUiModelFactory`() = - runBlocking { - val taskList = - listOf( - Task( - id = 42L, - name = "Task Name", - notes = "Notes", - ), - ) - val taskUiModel = - TaskUiModel( - taskId = 42L, - taskName = "Task Name", - ) - prepareScenario( - taskListUiModel = listOf(taskUiModel), - subhead = "Today", + fun `should create a subhead and a task when create is called from taskListUiModelFactory`() = runBlocking { + val taskList = + listOf( + Task( + id = 42L, + name = "Task Name", + notes = "Notes", + ), ) + val taskUiModel = + TaskUiModel( + taskId = 42L, + taskName = "Task Name", + ) + prepareScenario( + taskListUiModel = listOf(taskUiModel), + subhead = "Today", + ) - val result = taskListUiModelFactory.create(taskList, TaskCategory.TODAY) + val result = taskListUiModelFactory.create(taskList, TaskCategory.TODAY) - val expected = - listOf( - SubheadUiModel("Today"), - taskUiModel, - ) - assertEquals(expected, result) - } + val expected = + listOf( + SubheadUiModel("Today"), + taskUiModel, + ) + assertEquals(expected, result) + } @Test - fun `should not create a subhead and a task when task list is empty`() = - runBlocking { - prepareScenario() + fun `should not create a subhead and a task when task list is empty`() = runBlocking { + prepareScenario() - val result = taskListUiModelFactory.create(emptyList(), TaskCategory.TODAY) + val result = taskListUiModelFactory.create(emptyList(), TaskCategory.TODAY) - assertEquals(emptyList(), result) - } + assertEquals(emptyList(), result) + } private fun prepareScenario( taskListUiModel: List = emptyList(), diff --git a/feature/task-list/impl/src/test/java/br/com/sailboat/todozy/feature/task/list/impl/presentation/mapper/TaskCategoryToStringMapperTest.kt b/feature/task-list/impl/src/test/java/br/com/sailboat/todozy/feature/task/list/impl/presentation/mapper/TaskCategoryToStringMapperTest.kt index 005107c7..7080b23d 100644 --- a/feature/task-list/impl/src/test/java/br/com/sailboat/todozy/feature/task/list/impl/presentation/mapper/TaskCategoryToStringMapperTest.kt +++ b/feature/task-list/impl/src/test/java/br/com/sailboat/todozy/feature/task/list/impl/presentation/mapper/TaskCategoryToStringMapperTest.kt @@ -17,48 +17,64 @@ internal class TaskCategoryToStringMapperTest { ) @Test - fun `should map String from task category BEFORE_TODAY`() = - runBlocking { - val taskCategoryString = "Before today" - prepareScenario(taskCategoryString = taskCategoryString) + fun `should map String from task category BEFORE_TODAY`() = runBlocking { + val taskCategoryString = "Before today" + prepareScenario(taskCategoryString = taskCategoryString) - val result = taskCategoryToStringMapper.map(TaskCategory.BEFORE_TODAY) + val result = taskCategoryToStringMapper.map(TaskCategory.BEFORE_TODAY) - assertEquals(taskCategoryString, result) - } + assertEquals(taskCategoryString, result) + } + + @Test + fun `should map String from task category TODAY`() = runBlocking { + val taskCategoryString = "Today" + prepareScenario(taskCategoryString = taskCategoryString) + + val result = taskCategoryToStringMapper.map(TaskCategory.TODAY) + + assertEquals(taskCategoryString, result) + } + + @Test + fun `should map String from task category BEFORE_NOW`() = runBlocking { + val taskCategoryString = "Before now" + prepareScenario(taskCategoryString = taskCategoryString) + + val result = taskCategoryToStringMapper.map(TaskCategory.BEFORE_NOW) + + assertEquals(taskCategoryString, result) + } @Test - fun `should map String from task category TODAY`() = - runBlocking { - val taskCategoryString = "Today" - prepareScenario(taskCategoryString = taskCategoryString) + fun `should map String from task category TOMORROW`() = runBlocking { + val taskCategoryString = "Tomorrow" + prepareScenario(taskCategoryString = taskCategoryString) - val result = taskCategoryToStringMapper.map(TaskCategory.TODAY) + val result = taskCategoryToStringMapper.map(TaskCategory.TOMORROW) - assertEquals(taskCategoryString, result) - } + assertEquals(taskCategoryString, result) + } @Test - fun `should map String from task category TOMORROW`() = - runBlocking { - val taskCategoryString = "Tomorrow" - prepareScenario(taskCategoryString = taskCategoryString) + fun `should map String from task category NEXT_DAYS`() = runBlocking { + val taskCategoryString = "Next days" + prepareScenario(taskCategoryString = taskCategoryString) - val result = taskCategoryToStringMapper.map(TaskCategory.TOMORROW) + val result = taskCategoryToStringMapper.map(TaskCategory.NEXT_DAYS) - assertEquals(taskCategoryString, result) - } + assertEquals(taskCategoryString, result) + } @Test - fun `should map String from task category NEXT_DAYS`() = - runBlocking { - val taskCategoryString = "Next days" - prepareScenario(taskCategoryString = taskCategoryString) + fun `should map String from task category WITH_ALARMS`() = runBlocking { + val taskCategoryString = "With alarms" + prepareScenario(taskCategoryString = taskCategoryString) - val result = taskCategoryToStringMapper.map(TaskCategory.NEXT_DAYS) + val result = taskCategoryToStringMapper.map(TaskCategory.WITH_ALARMS) - assertEquals(taskCategoryString, result) - } + assertEquals(taskCategoryString, result) + } private fun prepareScenario(taskCategoryString: String = "Today") { coEvery { stringProvider.getString(any()) } returns taskCategoryString diff --git a/feature/task-list/impl/src/test/java/br/com/sailboat/todozy/feature/task/list/impl/presentation/viewmodel/TaskListViewModelTest.kt b/feature/task-list/impl/src/test/java/br/com/sailboat/todozy/feature/task/list/impl/presentation/viewmodel/TaskListViewModelTest.kt index 9e9243db..39dc0369 100644 --- a/feature/task-list/impl/src/test/java/br/com/sailboat/todozy/feature/task/list/impl/presentation/viewmodel/TaskListViewModelTest.kt +++ b/feature/task-list/impl/src/test/java/br/com/sailboat/todozy/feature/task/list/impl/presentation/viewmodel/TaskListViewModelTest.kt @@ -2,15 +2,12 @@ package br.com.sailboat.todozy.feature.task.list.impl.presentation.viewmodel import androidx.arch.core.executor.testing.InstantTaskExecutorRule import androidx.lifecycle.Observer -import br.com.sailboat.todozy.domain.model.Alarm -import br.com.sailboat.todozy.domain.model.RepeatType import br.com.sailboat.todozy.domain.model.Task import br.com.sailboat.todozy.domain.model.TaskCategory import br.com.sailboat.todozy.domain.model.TaskFilter import br.com.sailboat.todozy.domain.model.TaskMetrics import br.com.sailboat.todozy.domain.model.TaskProgressRange import br.com.sailboat.todozy.domain.model.TaskStatus -import br.com.sailboat.todozy.feature.alarm.domain.usecase.GetAlarmUseCase import br.com.sailboat.todozy.feature.alarm.domain.usecase.ScheduleAllAlarmsUseCase import br.com.sailboat.todozy.feature.task.details.domain.usecase.GetTaskMetricsUseCase import br.com.sailboat.todozy.feature.task.history.domain.model.TaskProgressFilter @@ -37,7 +34,6 @@ import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Rule import org.junit.Test -import java.util.Calendar import kotlin.test.assertEquals import kotlin.test.assertTrue @@ -52,7 +48,6 @@ internal class TaskListViewModelTest { val coroutinesTestRule = CoroutinesTestRule() private val getTasksUseCase: GetTasksUseCase = mockk(relaxed = true) - private val getAlarmUseCase: GetAlarmUseCase = mockk(relaxed = true) private val scheduleAllAlarmsUseCase: ScheduleAllAlarmsUseCase = mockk(relaxed = true) private val getTaskMetricsUseCase: GetTaskMetricsUseCase = mockk(relaxed = true) private val getTaskProgressUseCase: GetTaskProgressUseCase = mockk(relaxed = true) @@ -67,7 +62,6 @@ internal class TaskListViewModelTest { viewModel = TaskListViewModel( getTasksUseCase = getTasksUseCase, - getAlarmUseCase = getAlarmUseCase, scheduleAllAlarmsUseCase = scheduleAllAlarmsUseCase, getTaskMetricsUseCase = getTaskMetricsUseCase, getTaskProgressUseCase = getTaskProgressUseCase, @@ -79,71 +73,67 @@ internal class TaskListViewModelTest { } @Test - fun `should send CloseNotifications when dispatchViewAction is called with OnStart`() = - runTest(coroutinesTestRule.dispatcher) { - prepareScenario() + fun `should send CloseNotifications when dispatchViewAction is called with OnStart`() = runTest(coroutinesTestRule.dispatcher) { + prepareScenario() - viewModel.dispatchViewIntent(TaskListViewIntent.OnStart) - advanceUntilIdle() + viewModel.dispatchViewIntent(TaskListViewIntent.OnStart) + advanceUntilIdle() - assertEquals( - TaskListViewAction.CloseNotifications, - viewModel.viewState.viewAction.value, - ) - } + assertEquals( + TaskListViewAction.CloseNotifications, + viewModel.viewState.viewAction.value, + ) + } @Test - fun `should call getTasksUseCase when dispatchViewAction is called with OnStart`() = - runTest(coroutinesTestRule.dispatcher) { - val tasksView = - mutableListOf(TaskUiModel(taskId = 543L, taskName = "Task 543")) - prepareScenario(tasksView = tasksView) - - viewModel.dispatchViewIntent(TaskListViewIntent.OnStart) - advanceUntilIdle() - - coVerifyOrder { - getTasksUseCase(TaskFilter(category = TaskCategory.BEFORE_TODAY)) - getTasksUseCase(TaskFilter(category = TaskCategory.TODAY)) - getTasksUseCase(TaskFilter(category = TaskCategory.TOMORROW)) - getTasksUseCase(TaskFilter(category = TaskCategory.NEXT_DAYS)) - } - val expected = - mutableListOf( - TaskUiModel(taskId = 543L, taskName = "Task 543"), - TaskUiModel(taskId = 543L, taskName = "Task 543"), - TaskUiModel(taskId = 543L, taskName = "Task 543"), - TaskUiModel(taskId = 543L, taskName = "Task 543"), - ) - assertEquals(expected, viewModel.viewState.itemsView.value) + fun `should call getTasksUseCase when dispatchViewAction is called with OnStart`() = runTest(coroutinesTestRule.dispatcher) { + val tasksView = + mutableListOf(TaskUiModel(taskId = 543L, taskName = "Task 543")) + prepareScenario(tasksView = tasksView) + + viewModel.dispatchViewIntent(TaskListViewIntent.OnStart) + advanceUntilIdle() + + coVerifyOrder { + getTasksUseCase(TaskFilter(category = TaskCategory.BEFORE_TODAY)) + getTasksUseCase(TaskFilter(category = TaskCategory.TODAY)) + getTasksUseCase(TaskFilter(category = TaskCategory.TOMORROW)) + getTasksUseCase(TaskFilter(category = TaskCategory.NEXT_DAYS)) } + val expected = + mutableListOf( + TaskUiModel(taskId = 543L, taskName = "Task 543"), + TaskUiModel(taskId = 543L, taskName = "Task 543"), + TaskUiModel(taskId = 543L, taskName = "Task 543"), + TaskUiModel(taskId = 543L, taskName = "Task 543"), + ) + assertEquals(expected, viewModel.viewState.itemsView.value) + } @Test - fun `should call scheduleAllAlarmsUseCase when dispatchViewAction is called with OnStart`() = - runTest(coroutinesTestRule.dispatcher) { - prepareScenario() + fun `should call scheduleAllAlarmsUseCase when dispatchViewAction is called with OnStart`() = runTest(coroutinesTestRule.dispatcher) { + prepareScenario() - viewModel.dispatchViewIntent(TaskListViewIntent.OnStart) - advanceUntilIdle() + viewModel.dispatchViewIntent(TaskListViewIntent.OnStart) + advanceUntilIdle() - coVerify(exactly = 1) { scheduleAllAlarmsUseCase() } - coVerify { getTaskProgressUseCase(TaskProgressFilter(TaskProgressRange.LAST_YEAR)) } - } + coVerify(exactly = 1) { scheduleAllAlarmsUseCase() } + coVerify { getTaskProgressUseCase(TaskProgressFilter(TaskProgressRange.LAST_YEAR)) } + } @Test - fun `should hide metrics and progress when no tasks are loaded`() = - runTest(coroutinesTestRule.dispatcher) { - prepareScenario(tasksView = emptyList(), tasksResult = Result.success(emptyList())) + fun `should hide metrics and progress when no tasks are loaded`() = runTest(coroutinesTestRule.dispatcher) { + prepareScenario(tasksView = emptyList(), tasksResult = Result.success(emptyList())) - viewModel.dispatchViewIntent(TaskListViewIntent.OnStart) - advanceUntilIdle() + viewModel.dispatchViewIntent(TaskListViewIntent.OnStart) + advanceUntilIdle() - assertEquals(null, viewModel.viewState.taskMetrics.value) - assertTrue(viewModel.viewState.taskProgressDays.value?.isEmpty() == true) - assertEquals(false, viewModel.viewState.taskProgressLoading.value) - coVerify(exactly = 0) { getTaskMetricsUseCase(any()) } - coVerify(exactly = 0) { getTaskProgressUseCase(any()) } - } + assertEquals(null, viewModel.viewState.taskMetrics.value) + assertTrue(viewModel.viewState.taskProgressDays.value?.isEmpty() == true) + assertEquals(false, viewModel.viewState.taskProgressLoading.value) + coVerify(exactly = 0) { getTaskMetricsUseCase(any()) } + coVerify(exactly = 0) { getTaskProgressUseCase(any()) } + } @Test fun `should navigate to about screen when dispatchViewAction is called with OnClickMenuAbout`() { @@ -277,67 +267,65 @@ internal class TaskListViewModelTest { } @Test - fun `should refresh metrics when dispatchViewAction is called with OnSwipeTask`() = - runTest(coroutinesTestRule.dispatcher) { - val tasks = - mutableListOf( - TaskUiModel(taskId = 543L, taskName = "Task 543"), - TaskUiModel(taskId = 978L, taskName = "Task 978"), - ) - val position = 1 - val status = TaskStatus.DONE - viewModel.viewState.itemsView.value = tasks - prepareScenario() - - viewModel.dispatchViewIntent(TaskListViewIntent.OnSwipeTask(position, status)) - advanceTimeBy(TASK_SWIPE_DELAY_IN_MILLIS) - advanceUntilIdle() + fun `should refresh metrics when dispatchViewAction is called with OnSwipeTask`() = runTest(coroutinesTestRule.dispatcher) { + val tasks = + mutableListOf( + TaskUiModel(taskId = 543L, taskName = "Task 543"), + TaskUiModel(taskId = 978L, taskName = "Task 978"), + ) + val position = 1 + val status = TaskStatus.DONE + viewModel.viewState.itemsView.value = tasks + prepareScenario() - coVerify { - getTaskMetricsUseCase( - match { filter -> - filter.taskId == Entity.NO_ID && - filter.initialDate != null && - filter.finalDate != null - }, - ) - } + viewModel.dispatchViewIntent(TaskListViewIntent.OnSwipeTask(position, status)) + advanceTimeBy(TASK_SWIPE_DELAY_IN_MILLIS) + advanceUntilIdle() + + coVerify { + getTaskMetricsUseCase( + match { filter -> + filter.taskId == Entity.NO_ID && + filter.initialDate != null && + filter.finalDate != null + }, + ) } + } @Test - fun `should show inline metrics when dispatchViewAction is called with OnSwipeTask`() = - runTest(coroutinesTestRule.dispatcher) { - val taskId = 978L - val tasks = - mutableListOf( - TaskUiModel(taskId = 543L, taskName = "Task 543"), - TaskUiModel(taskId = taskId, taskName = "Task 978"), - ) - val inlineMetrics = TaskMetrics(doneTasks = 2, notDoneTasks = 1, consecutiveDone = 3) - prepareScenario( - tasksView = tasks, - tasksResult = - Result.success( - listOf( - Task(id = 543L, name = "Task 543", notes = null), - Task(id = taskId, name = "Task 978", notes = null), - ), - ), + fun `should show inline metrics when dispatchViewAction is called with OnSwipeTask`() = runTest(coroutinesTestRule.dispatcher) { + val taskId = 978L + val tasks = + mutableListOf( + TaskUiModel(taskId = 543L, taskName = "Task 543"), + TaskUiModel(taskId = taskId, taskName = "Task 978"), ) - coEvery { - getTaskMetricsUseCase(match { it.taskId == taskId }) - } returns Result.success(inlineMetrics) + val inlineMetrics = TaskMetrics(doneTasks = 2, notDoneTasks = 1, consecutiveDone = 3) + prepareScenario( + tasksView = tasks, + tasksResult = + Result.success( + listOf( + Task(id = 543L, name = "Task 543", notes = null), + Task(id = taskId, name = "Task 978", notes = null), + ), + ), + ) + coEvery { + getTaskMetricsUseCase(match { it.taskId == taskId }) + } returns Result.success(inlineMetrics) - viewModel.viewState.itemsView.value = tasks + viewModel.viewState.itemsView.value = tasks - viewModel.dispatchViewIntent(TaskListViewIntent.OnSwipeTask(1, TaskStatus.DONE)) - runCurrent() + viewModel.dispatchViewIntent(TaskListViewIntent.OnSwipeTask(1, TaskStatus.DONE)) + runCurrent() - val updatedTask = viewModel.viewState.itemsView.value?.get(1) as TaskUiModel - assertTrue(updatedTask.showInlineMetrics) - assertEquals(TaskMetrics(doneTasks = 3, notDoneTasks = 1, consecutiveDone = 4), updatedTask.inlineMetrics) - assertEquals(TaskStatus.DONE, updatedTask.inlineStatus) - } + val updatedTask = viewModel.viewState.itemsView.value?.get(1) as TaskUiModel + assertTrue(updatedTask.showInlineMetrics) + assertEquals(TaskMetrics(doneTasks = 3, notDoneTasks = 1, consecutiveDone = 4), updatedTask.inlineMetrics) + assertEquals(TaskStatus.DONE, updatedTask.inlineStatus) + } @Test fun `should call getTaskMetricsUseCase when dispatchViewAction is called with OnSwipeTask on a task with repetitive alarm`() { @@ -347,18 +335,10 @@ internal class TaskListViewModelTest { TaskUiModel(taskId = 543L, taskName = "Task 543"), TaskUiModel(taskId = 978L, taskName = "Task 978"), ) - val alarmResult = - Result.success( - Alarm( - dateTime = Calendar.getInstance(), - repeatType = RepeatType.WEEK, - ), - ) - val position = 1 val status = TaskStatus.DONE viewModel.viewState.itemsView.value = tasks - prepareScenario(alarmResult = alarmResult) + prepareScenario() viewModel.dispatchViewIntent(TaskListViewIntent.OnSwipeTask(position, status)) advanceUntilIdle() @@ -376,117 +356,113 @@ internal class TaskListViewModelTest { } @Test - fun `should show optimistic metrics while commit is pending`() = - runTest(coroutinesTestRule.dispatcher) { - val tasks = - mutableListOf( - TaskUiModel(taskId = 543L, taskName = "Task 543"), - ) - prepareScenario(tasksView = tasks) + fun `should show optimistic metrics while commit is pending`() = runTest(coroutinesTestRule.dispatcher) { + val tasks = + mutableListOf( + TaskUiModel(taskId = 543L, taskName = "Task 543"), + ) + prepareScenario(tasksView = tasks) - viewModel.dispatchViewIntent(TaskListViewIntent.OnStart) - advanceUntilIdle() + viewModel.dispatchViewIntent(TaskListViewIntent.OnStart) + advanceUntilIdle() - viewModel.dispatchViewIntent(TaskListViewIntent.OnSwipeTask(position = 0, status = TaskStatus.DONE)) - runCurrent() + viewModel.dispatchViewIntent(TaskListViewIntent.OnSwipeTask(position = 0, status = TaskStatus.DONE)) + runCurrent() - assertEquals(TaskMetrics(doneTasks = 1, notDoneTasks = 0, consecutiveDone = 1), viewModel.viewState.taskMetrics.value) - } + assertEquals(TaskMetrics(doneTasks = 1, notDoneTasks = 0, consecutiveDone = 1), viewModel.viewState.taskMetrics.value) + } @Test - fun `should undo swipe and restore task`() = - runTest(coroutinesTestRule.dispatcher) { - val taskId = 978L - val tasks = - mutableListOf( - TaskUiModel(taskId = taskId, taskName = "Task 978"), - ) - prepareScenario( - tasksView = tasks, - tasksResult = - Result.success( - listOf( - Task(id = taskId, name = "Task 978", notes = null), - ), - ), + fun `should undo swipe and restore task`() = runTest(coroutinesTestRule.dispatcher) { + val taskId = 978L + val tasks = + mutableListOf( + TaskUiModel(taskId = taskId, taskName = "Task 978"), ) + prepareScenario( + tasksView = tasks, + tasksResult = + Result.success( + listOf( + Task(id = taskId, name = "Task 978", notes = null), + ), + ), + ) - viewModel.viewState.itemsView.value = tasks + viewModel.viewState.itemsView.value = tasks - viewModel.dispatchViewIntent(TaskListViewIntent.OnSwipeTask(0, TaskStatus.DONE)) - runCurrent() + viewModel.dispatchViewIntent(TaskListViewIntent.OnSwipeTask(0, TaskStatus.DONE)) + runCurrent() - viewModel.dispatchViewIntent(TaskListViewIntent.OnClickUndoTask(taskId, TaskStatus.DONE)) - advanceTimeBy(TASK_SWIPE_DELAY_IN_MILLIS) - advanceUntilIdle() + viewModel.dispatchViewIntent(TaskListViewIntent.OnClickUndoTask(taskId, TaskStatus.DONE)) + advanceTimeBy(TASK_SWIPE_DELAY_IN_MILLIS) + advanceUntilIdle() - coVerify(exactly = 0) { completeTaskUseCase(taskId, any()) } - val restoredTask = - viewModel.viewState.itemsView.value?.filterIsInstance()?.firstOrNull() - assertTrue(restoredTask?.showInlineMetrics == false) - } + coVerify(exactly = 0) { completeTaskUseCase(taskId, any()) } + val restoredTask = + viewModel.viewState.itemsView.value?.filterIsInstance()?.firstOrNull() + assertTrue(restoredTask?.showInlineMetrics == false) + } @Test - fun `should reload progress when progress range changes`() = - runTest(coroutinesTestRule.dispatcher) { - prepareScenario() + fun `should reload progress when progress range changes`() = runTest(coroutinesTestRule.dispatcher) { + prepareScenario() - viewModel.dispatchViewIntent(TaskListViewIntent.OnStart) - advanceUntilIdle() + viewModel.dispatchViewIntent(TaskListViewIntent.OnStart) + advanceUntilIdle() - viewModel.dispatchViewIntent(TaskListViewIntent.OnSelectProgressRange(TaskProgressRange.LAST_30_DAYS)) - advanceUntilIdle() + viewModel.dispatchViewIntent(TaskListViewIntent.OnSelectProgressRange(TaskProgressRange.LAST_30_DAYS)) + advanceUntilIdle() - coVerify { - getTaskProgressUseCase(TaskProgressFilter(range = TaskProgressRange.LAST_30_DAYS)) - } - assertEquals(TaskProgressRange.LAST_30_DAYS, viewModel.viewState.taskProgressRange.value) + coVerify { + getTaskProgressUseCase(TaskProgressFilter(range = TaskProgressRange.LAST_30_DAYS)) } + assertEquals(TaskProgressRange.LAST_30_DAYS, viewModel.viewState.taskProgressRange.value) + } @Test - fun `should sum consecutive done across tasks when loading metrics`() = - runTest(coroutinesTestRule.dispatcher) { - val taskB = - Task( - id = 101L, - name = "Task B", - notes = null, - ) - val taskC = - Task( - id = 202L, - name = "Task C", - notes = null, - ) - val taskD = - Task( - id = 303L, - name = "Task D", - notes = null, - ) - - coEvery { getTasksUseCase(match { it.category == TaskCategory.TODAY }) } returns - Result.success(listOf(taskB, taskC, taskD)) - coEvery { getTasksUseCase(match { it.category != TaskCategory.TODAY }) } returns Result.success(emptyList()) - coEvery { taskListUiModelFactory.create(any(), any()) } returns emptyList() - coEvery { getTaskProgressUseCase(any()) } returns Result.success(emptyList()) - coEvery { getTaskMetricsUseCase(match { it.taskId == Entity.NO_ID }) } returns - Result.success(TaskMetrics(doneTasks = 70, notDoneTasks = 5, consecutiveDone = 0)) - coEvery { getTaskMetricsUseCase(match { it.taskId == taskB.id }) } returns - Result.success(TaskMetrics(doneTasks = 10, notDoneTasks = 0, consecutiveDone = 10)) - coEvery { getTaskMetricsUseCase(match { it.taskId == taskC.id }) } returns - Result.success(TaskMetrics(doneTasks = 20, notDoneTasks = 0, consecutiveDone = 20)) - coEvery { getTaskMetricsUseCase(match { it.taskId == taskD.id }) } returns - Result.success(TaskMetrics(doneTasks = 40, notDoneTasks = 0, consecutiveDone = 40)) - - viewModel.dispatchViewIntent(TaskListViewIntent.OnStart) - advanceUntilIdle() - - assertEquals( - TaskMetrics(doneTasks = 70, notDoneTasks = 5, consecutiveDone = 70), - viewModel.viewState.taskMetrics.value, + fun `should sum consecutive done across tasks when loading metrics`() = runTest(coroutinesTestRule.dispatcher) { + val taskB = + Task( + id = 101L, + name = "Task B", + notes = null, ) - } + val taskC = + Task( + id = 202L, + name = "Task C", + notes = null, + ) + val taskD = + Task( + id = 303L, + name = "Task D", + notes = null, + ) + + coEvery { getTasksUseCase(match { it.category == TaskCategory.TODAY }) } returns + Result.success(listOf(taskB, taskC, taskD)) + coEvery { getTasksUseCase(match { it.category != TaskCategory.TODAY }) } returns Result.success(emptyList()) + coEvery { taskListUiModelFactory.create(any(), any()) } returns emptyList() + coEvery { getTaskProgressUseCase(any()) } returns Result.success(emptyList()) + coEvery { getTaskMetricsUseCase(match { it.taskId == Entity.NO_ID }) } returns + Result.success(TaskMetrics(doneTasks = 70, notDoneTasks = 5, consecutiveDone = 0)) + coEvery { getTaskMetricsUseCase(match { it.taskId == taskB.id }) } returns + Result.success(TaskMetrics(doneTasks = 10, notDoneTasks = 0, consecutiveDone = 10)) + coEvery { getTaskMetricsUseCase(match { it.taskId == taskC.id }) } returns + Result.success(TaskMetrics(doneTasks = 20, notDoneTasks = 0, consecutiveDone = 20)) + coEvery { getTaskMetricsUseCase(match { it.taskId == taskD.id }) } returns + Result.success(TaskMetrics(doneTasks = 40, notDoneTasks = 0, consecutiveDone = 40)) + + viewModel.dispatchViewIntent(TaskListViewIntent.OnStart) + advanceUntilIdle() + + assertEquals( + TaskMetrics(doneTasks = 70, notDoneTasks = 5, consecutiveDone = 70), + viewModel.viewState.taskMetrics.value, + ) + } private fun prepareScenario( tasksView: List = @@ -506,18 +482,11 @@ internal class TaskListViewModelTest { ), ), ), - alarmResult: Result = - Result.success( - Alarm( - dateTime = Calendar.getInstance(), - repeatType = RepeatType.WEEK, - ), - ), ) { coEvery { getTasksUseCase(any()) } returns tasksResult - coEvery { getAlarmUseCase(any()) } returns alarmResult coEvery { taskListUiModelFactory.create(any(), any()) } returns tasksView coEvery { getTaskProgressUseCase(any()) } returns Result.success(emptyList()) coEvery { getTaskMetricsUseCase(any()) } returns Result.success(TaskMetrics(0, 0, 0)) + coEvery { completeTaskUseCase(any(), any()) } returns Result.success(Unit) } } diff --git a/ui-component/impl/src/main/java/br/com/sailboat/uicomponent/impl/dialog/selectable/SelectableItemViewHolder.kt b/ui-component/impl/src/main/java/br/com/sailboat/uicomponent/impl/dialog/selectable/SelectableItemViewHolder.kt index 3ca7f424..7c78ae37 100644 --- a/ui-component/impl/src/main/java/br/com/sailboat/uicomponent/impl/dialog/selectable/SelectableItemViewHolder.kt +++ b/ui-component/impl/src/main/java/br/com/sailboat/uicomponent/impl/dialog/selectable/SelectableItemViewHolder.kt @@ -16,16 +16,15 @@ class SelectableItemViewHolder(parent: ViewGroup, private val callback: Callback fun onClickItem(position: Int) } - override fun bind(item: SelectableItem) = - with(binding) { - itemView.setOnClickListener { callback.onClickItem(bindingAdapterPosition) } - tvSelectableItemName.setText(item.getName()) + override fun bind(item: SelectableItem) = with(binding) { + itemView.setOnClickListener { callback.onClickItem(bindingAdapterPosition) } + tvSelectableItemName.setText(item.getName()) - val selectedItem = callback.selectedItem - if (selectedItem != null && selectedItem === item) { - ivSelectableItemSelected.visible() - } else { - ivSelectableItemSelected.gone() - } + val selectedItem = callback.selectedItem + if (selectedItem != null && selectedItem === item) { + ivSelectableItemSelected.visible() + } else { + ivSelectableItemSelected.gone() } + } } diff --git a/ui-component/impl/src/main/java/br/com/sailboat/uicomponent/impl/dialog/weekdays/WeekDayViewHolder.kt b/ui-component/impl/src/main/java/br/com/sailboat/uicomponent/impl/dialog/weekdays/WeekDayViewHolder.kt index 033ee82a..a040d182 100644 --- a/ui-component/impl/src/main/java/br/com/sailboat/uicomponent/impl/dialog/weekdays/WeekDayViewHolder.kt +++ b/ui-component/impl/src/main/java/br/com/sailboat/uicomponent/impl/dialog/weekdays/WeekDayViewHolder.kt @@ -11,29 +11,28 @@ class WeekDayViewHolder(parent: ViewGroup, private val callback: Callback) : BaseViewHolder( VhWeekDayBinding.inflate(getInflater(parent), parent, false), ) { - override fun bind(item: DayUiModel) = - with(binding) { - root.setOnClickListener { callback.onClickDay(bindingAdapterPosition) } - tvWeekDayName.text = item.name + override fun bind(item: DayUiModel) = with(binding) { + root.setOnClickListener { callback.onClickDay(bindingAdapterPosition) } + tvWeekDayName.text = item.name - if (callback.isDaySelected(item.id)) { - tvWeekDayName.setBackgroundResource(R.drawable.shape_circle_blue) - tvWeekDayName.setTextColor( - ContextCompat.getColor( - itemView.context, - android.R.color.white, - ), - ) - } else { - tvWeekDayName.setBackgroundResource(R.drawable.shape_circle) - tvWeekDayName.setTextColor( - ContextCompat.getColor( - itemView.context, - R.color.md_blue_grey_500, - ), - ) - } + if (callback.isDaySelected(item.id)) { + tvWeekDayName.setBackgroundResource(R.drawable.shape_circle_blue) + tvWeekDayName.setTextColor( + ContextCompat.getColor( + itemView.context, + android.R.color.white, + ), + ) + } else { + tvWeekDayName.setBackgroundResource(R.drawable.shape_circle) + tvWeekDayName.setTextColor( + ContextCompat.getColor( + itemView.context, + R.color.md_blue_grey_500, + ), + ) } + } interface Callback { fun isDaySelected(id: Int): Boolean diff --git a/ui-component/impl/src/main/java/br/com/sailboat/uicomponent/impl/dialog/weekdays/WeekDaysSelectorDialog.kt b/ui-component/impl/src/main/java/br/com/sailboat/uicomponent/impl/dialog/weekdays/WeekDaysSelectorDialog.kt index 3d3cf572..10429066 100644 --- a/ui-component/impl/src/main/java/br/com/sailboat/uicomponent/impl/dialog/weekdays/WeekDaysSelectorDialog.kt +++ b/ui-component/impl/src/main/java/br/com/sailboat/uicomponent/impl/dialog/weekdays/WeekDaysSelectorDialog.kt @@ -35,12 +35,11 @@ class WeekDaysSelectorDialog(private val callback: Callback) : return buildDialog() } - private fun initViews() = - with(binding) { - recycler.layoutManager = - GridLayoutManager(activity, 2, LinearLayoutManager.HORIZONTAL, false) - recycler.adapter = WeekDaysSelectorAdapter(this@WeekDaysSelectorDialog) - } + private fun initViews() = with(binding) { + recycler.layoutManager = + GridLayoutManager(activity, 2, LinearLayoutManager.HORIZONTAL, false) + recycler.adapter = WeekDaysSelectorAdapter(this@WeekDaysSelectorDialog) + } private fun buildDialog(): Dialog { val builder = AlertDialog.Builder(requireContext()) @@ -103,17 +102,16 @@ class WeekDaysSelectorDialog(private val callback: Callback) : binding.recycler.adapter?.notifyItemChanged(position) } - private fun getDayViewFromId(id: Int) = - when (id) { - Calendar.SUNDAY -> DayUiModel(id, getString(R.string.sunday)) - Calendar.MONDAY -> DayUiModel(id, getString(R.string.monday)) - Calendar.TUESDAY -> DayUiModel(id, getString(R.string.tuesday)) - Calendar.WEDNESDAY -> DayUiModel(id, getString(R.string.wednesday)) - Calendar.THURSDAY -> DayUiModel(id, getString(R.string.thursday)) - Calendar.FRIDAY -> DayUiModel(id, getString(R.string.friday)) - Calendar.SATURDAY -> DayUiModel(id, getString(R.string.saturday)) - else -> null - } + private fun getDayViewFromId(id: Int) = when (id) { + Calendar.SUNDAY -> DayUiModel(id, getString(R.string.sunday)) + Calendar.MONDAY -> DayUiModel(id, getString(R.string.monday)) + Calendar.TUESDAY -> DayUiModel(id, getString(R.string.tuesday)) + Calendar.WEDNESDAY -> DayUiModel(id, getString(R.string.wednesday)) + Calendar.THURSDAY -> DayUiModel(id, getString(R.string.thursday)) + Calendar.FRIDAY -> DayUiModel(id, getString(R.string.friday)) + Calendar.SATURDAY -> DayUiModel(id, getString(R.string.saturday)) + else -> null + } interface Callback { fun onClickOk(selectedDays: String) diff --git a/ui-component/impl/src/main/java/br/com/sailboat/uicomponent/impl/progress/TaskProgressComponents.kt b/ui-component/impl/src/main/java/br/com/sailboat/uicomponent/impl/progress/TaskProgressComponents.kt index d73aa3d8..b09f5578 100644 --- a/ui-component/impl/src/main/java/br/com/sailboat/uicomponent/impl/progress/TaskProgressComponents.kt +++ b/ui-component/impl/src/main/java/br/com/sailboat/uicomponent/impl/progress/TaskProgressComponents.kt @@ -507,13 +507,12 @@ private data class DayHeatmapMetadata( ) @Composable -private fun TaskProgressRange.toLabel(): String = - when (this) { - TaskProgressRange.ALL -> stringResource(R.string.all_days) - TaskProgressRange.LAST_YEAR -> stringResource(R.string.last_12_months) - TaskProgressRange.LAST_30_DAYS -> stringResource(R.string.last_30_days) - TaskProgressRange.LAST_7_DAYS -> stringResource(R.string.last_7_days) - } +private fun TaskProgressRange.toLabel(): String = when (this) { + TaskProgressRange.ALL -> stringResource(R.string.all_days) + TaskProgressRange.LAST_YEAR -> stringResource(R.string.last_12_months) + TaskProgressRange.LAST_30_DAYS -> stringResource(R.string.last_30_days) + TaskProgressRange.LAST_7_DAYS -> stringResource(R.string.last_7_days) +} private fun TaskProgressDay.color( palette: ProgressPalette, diff --git a/ui-component/impl/src/main/java/br/com/sailboat/uicomponent/impl/progress/TaskProgressHeaderAdapter.kt b/ui-component/impl/src/main/java/br/com/sailboat/uicomponent/impl/progress/TaskProgressHeaderAdapter.kt index 39b2bcd0..94c0ff1a 100644 --- a/ui-component/impl/src/main/java/br/com/sailboat/uicomponent/impl/progress/TaskProgressHeaderAdapter.kt +++ b/ui-component/impl/src/main/java/br/com/sailboat/uicomponent/impl/progress/TaskProgressHeaderAdapter.kt @@ -32,12 +32,11 @@ class TaskProgressHeaderAdapter( notifyDataSetChanged() } - override fun getItemCount(): Int = - if (days.isEmpty() && isLoading.not()) { - 0 - } else { - 1 - } + override fun getItemCount(): Int = if (days.isEmpty() && isLoading.not()) { + 0 + } else { + 1 + } override fun onCreateViewHolder( parent: ViewGroup, diff --git a/ui-component/impl/src/main/java/br/com/sailboat/uicomponent/impl/skeleton/TaskSkeletonItem.kt b/ui-component/impl/src/main/java/br/com/sailboat/uicomponent/impl/skeleton/TaskSkeletonItem.kt new file mode 100644 index 00000000..4f753d07 --- /dev/null +++ b/ui-component/impl/src/main/java/br/com/sailboat/uicomponent/impl/skeleton/TaskSkeletonItem.kt @@ -0,0 +1,73 @@ +package br.com.sailboat.uicomponent.impl.skeleton + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.heightIn +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.unit.dp + +@Composable +@Suppress("FunctionName") +fun TaskSkeletonItem() { + val baseColor = Color(0xFFE6E6E6) + val cardColor = Color.White + + Row( + modifier = + Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp, vertical = 6.dp) + .heightIn(min = 72.dp) + .background(color = cardColor, shape = RoundedCornerShape(12.dp)) + .padding(16.dp), + verticalAlignment = Alignment.CenterVertically, + ) { + Box( + modifier = + Modifier + .width(40.dp) + .height(40.dp) + .background(color = baseColor, shape = RoundedCornerShape(20.dp)), + ) + + Column( + modifier = Modifier.weight(1f), + horizontalAlignment = Alignment.Start, + ) { + Box( + modifier = + Modifier + .fillMaxWidth(0.75f) + .height(14.dp) + .background(color = baseColor, shape = RoundedCornerShape(8.dp)), + ) + Spacer(modifier = Modifier.height(8.dp)) + Box( + modifier = + Modifier + .fillMaxWidth(0.5f) + .height(12.dp) + .background(color = baseColor, shape = RoundedCornerShape(8.dp)), + ) + } + + Box( + modifier = + Modifier + .width(64.dp) + .height(20.dp) + .background(color = baseColor, shape = RoundedCornerShape(6.dp)), + ) + } +} diff --git a/ui-component/impl/src/main/java/br/com/sailboat/uicomponent/impl/viewholder/AlarmViewHolder.kt b/ui-component/impl/src/main/java/br/com/sailboat/uicomponent/impl/viewholder/AlarmViewHolder.kt index d65a9bd6..0c5337ed 100644 --- a/ui-component/impl/src/main/java/br/com/sailboat/uicomponent/impl/viewholder/AlarmViewHolder.kt +++ b/ui-component/impl/src/main/java/br/com/sailboat/uicomponent/impl/viewholder/AlarmViewHolder.kt @@ -12,26 +12,24 @@ class AlarmViewHolder(parent: ViewGroup) : BaseViewHolder( AlarmDetailsBinding.inflate(getInflater(parent), parent, false), ) { - override fun bind(item: AlarmUiModel): Unit = - with(binding) { - try { - tvAlarmDate.text = item.date - tvAlarmTime.text = item.time + override fun bind(item: AlarmUiModel): Unit = with(binding) { + try { + tvAlarmDate.text = item.date + tvAlarmTime.text = item.time - updateAlarmRepeatType(item) - } catch (e: Exception) { - e.log() - } + updateAlarmRepeatType(item) + } catch (e: Exception) { + e.log() } + } - private fun updateAlarmRepeatType(alarm: AlarmUiModel) = - with(binding) { - if (alarm.shouldRepeat) { - tvAlarmRepeat.visible() + private fun updateAlarmRepeatType(alarm: AlarmUiModel) = with(binding) { + if (alarm.shouldRepeat) { + tvAlarmRepeat.visible() - tvAlarmRepeat.text = alarm.description - } else { - tvAlarmRepeat.gone() - } + tvAlarmRepeat.text = alarm.description + } else { + tvAlarmRepeat.gone() } + } } diff --git a/ui-component/impl/src/main/java/br/com/sailboat/uicomponent/impl/viewholder/ImageTitleDividerViewHolder.kt b/ui-component/impl/src/main/java/br/com/sailboat/uicomponent/impl/viewholder/ImageTitleDividerViewHolder.kt index bd786558..194989ed 100644 --- a/ui-component/impl/src/main/java/br/com/sailboat/uicomponent/impl/viewholder/ImageTitleDividerViewHolder.kt +++ b/ui-component/impl/src/main/java/br/com/sailboat/uicomponent/impl/viewholder/ImageTitleDividerViewHolder.kt @@ -9,9 +9,8 @@ class ImageTitleDividerViewHolder(parent: ViewGroup) : BaseViewHolder( VhImageTitleDividerBinding.inflate(getInflater(parent), parent, false), ) { - override fun bind(item: ImageTitleDividerUiModel) = - with(binding) { - vhImageTitleDividerImg.setImageResource(item.imageRes) - vhImageTitleDividerTvTitle.text = item.title - } + override fun bind(item: ImageTitleDividerUiModel) = with(binding) { + vhImageTitleDividerImg.setImageResource(item.imageRes) + vhImageTitleDividerTvTitle.text = item.title + } } diff --git a/ui-component/impl/src/main/java/br/com/sailboat/uicomponent/impl/viewholder/LabelValueViewHolder.kt b/ui-component/impl/src/main/java/br/com/sailboat/uicomponent/impl/viewholder/LabelValueViewHolder.kt index 671de640..08c95005 100644 --- a/ui-component/impl/src/main/java/br/com/sailboat/uicomponent/impl/viewholder/LabelValueViewHolder.kt +++ b/ui-component/impl/src/main/java/br/com/sailboat/uicomponent/impl/viewholder/LabelValueViewHolder.kt @@ -9,9 +9,8 @@ class LabelValueViewHolder(parent: ViewGroup) : BaseViewHolder( VhLabelValueBinding.inflate(getInflater(parent), parent, false), ) { - override fun bind(item: LabelValueUiModel) = - with(binding) { - vhLabelValueTvLabel.text = item.label - vhLabelValueTvValue.text = item.value - } + override fun bind(item: LabelValueUiModel) = with(binding) { + vhLabelValueTvLabel.text = item.label + vhLabelValueTvValue.text = item.value + } } diff --git a/ui-component/impl/src/main/java/br/com/sailboat/uicomponent/impl/viewholder/LabelViewHolder.kt b/ui-component/impl/src/main/java/br/com/sailboat/uicomponent/impl/viewholder/LabelViewHolder.kt index 66dd1177..57ad51a3 100644 --- a/ui-component/impl/src/main/java/br/com/sailboat/uicomponent/impl/viewholder/LabelViewHolder.kt +++ b/ui-component/impl/src/main/java/br/com/sailboat/uicomponent/impl/viewholder/LabelViewHolder.kt @@ -8,8 +8,7 @@ import br.com.sailboat.uicomponent.model.LabelUiModel class LabelViewHolder(parent: ViewGroup) : BaseViewHolder( VhLabelBinding.inflate(getInflater(parent), parent, false), ) { - override fun bind(item: LabelUiModel) = - with(binding) { - vhLabelTvLabel.text = item.label - } + override fun bind(item: LabelUiModel) = with(binding) { + vhLabelTvLabel.text = item.label + } } diff --git a/ui-component/impl/src/main/java/br/com/sailboat/uicomponent/impl/viewholder/SubheadViewHolder.kt b/ui-component/impl/src/main/java/br/com/sailboat/uicomponent/impl/viewholder/SubheadViewHolder.kt index baf3bfbb..96f5258f 100644 --- a/ui-component/impl/src/main/java/br/com/sailboat/uicomponent/impl/viewholder/SubheadViewHolder.kt +++ b/ui-component/impl/src/main/java/br/com/sailboat/uicomponent/impl/viewholder/SubheadViewHolder.kt @@ -9,8 +9,7 @@ class SubheadViewHolder(parent: ViewGroup) : BaseViewHolder( VhSubheaderBinding.inflate(getInflater(parent), parent, false), ) { - override fun bind(item: SubheadUiModel) = - with(binding) { - vhSubheaderTvName.text = item.subhead - } + override fun bind(item: SubheadUiModel) = with(binding) { + vhSubheaderTvName.text = item.subhead + } } diff --git a/ui-component/impl/src/main/java/br/com/sailboat/uicomponent/impl/viewholder/TaskHistoryViewHolder.kt b/ui-component/impl/src/main/java/br/com/sailboat/uicomponent/impl/viewholder/TaskHistoryViewHolder.kt index 94c46f54..0119bb4b 100644 --- a/ui-component/impl/src/main/java/br/com/sailboat/uicomponent/impl/viewholder/TaskHistoryViewHolder.kt +++ b/ui-component/impl/src/main/java/br/com/sailboat/uicomponent/impl/viewholder/TaskHistoryViewHolder.kt @@ -21,78 +21,72 @@ class TaskHistoryViewHolder( BaseViewHolder( VhTaskHistoryBinding.inflate(getInflater(parent), parent, false), ) { - override fun bind(item: TaskHistoryUiModel) = - with(binding) { - root.setOnClickListener { callback.onClickHistory(bindingAdapterPosition) } - tvTaskHistoryDelete.setOnClickListener { callback.onClickDelete(bindingAdapterPosition) } + override fun bind(item: TaskHistoryUiModel) = with(binding) { + root.setOnClickListener { callback.onClickHistory(bindingAdapterPosition) } + tvTaskHistoryDelete.setOnClickListener { callback.onClickDelete(bindingAdapterPosition) } - tvTaskHistoryName.text = item.taskName - setStatus(item) - setOptions(item) - setDateTimeTask(item) - } + tvTaskHistoryName.text = item.taskName + setStatus(item) + setOptions(item) + setDateTimeTask(item) + } - private fun setStatus(history: TaskHistoryUiModel) = - with(binding) { - if (history.done) { - ivTaskHistoryStatus.setImageResource(R.drawable.ic_vec_thumb_up_white_24dp) - ivTaskHistoryStatus.setBackgroundResource(R.drawable.shape_circle_done_task) - } else { - ivTaskHistoryStatus.setImageResource(R.drawable.ic_vect_thumb_down_white_24dp) - ivTaskHistoryStatus.setBackgroundResource(R.drawable.shape_circle_not_done) - } + private fun setStatus(history: TaskHistoryUiModel) = with(binding) { + if (history.done) { + ivTaskHistoryStatus.setImageResource(R.drawable.ic_vec_thumb_up_white_24dp) + ivTaskHistoryStatus.setBackgroundResource(R.drawable.shape_circle_done_task) + } else { + ivTaskHistoryStatus.setImageResource(R.drawable.ic_vect_thumb_down_white_24dp) + ivTaskHistoryStatus.setBackgroundResource(R.drawable.shape_circle_not_done) } + } - private fun setDateTimeTask(history: TaskHistoryUiModel) = - with(binding) { - try { - val calendar = history.insertingDate.toDateTimeCalendar() - tvTaskHistoryLongDateTime.text = formatter.formatFull(calendar) - tvTaskHistoryShortDateTime.text = formatter.formatShort(calendar) - } catch (e: ParseException) { - e.printStackTrace() - tvTaskHistoryShortDateTime.gone() - } + private fun setDateTimeTask(history: TaskHistoryUiModel) = with(binding) { + try { + val calendar = history.insertingDate.toDateTimeCalendar() + tvTaskHistoryLongDateTime.text = formatter.formatFull(calendar) + tvTaskHistoryShortDateTime.text = formatter.formatShort(calendar) + } catch (e: ParseException) { + e.printStackTrace() + tvTaskHistoryShortDateTime.gone() } + } - private fun setOptions(history: TaskHistoryUiModel) = - with(binding) { - if (callback.isShowingOptions(bindingAdapterPosition)) { - tvTaskHistoryShortDateTime.gone() - tvTaskHistoryLongDateTime.visible() - llTaskHistoryActions.visible() - tvTaskHistoryName.maxLines = Integer.MAX_VALUE - tvTaskHistoryName.ellipsize = null - (itemView as CardView).cardElevation = 6f + private fun setOptions(history: TaskHistoryUiModel) = with(binding) { + if (callback.isShowingOptions(bindingAdapterPosition)) { + tvTaskHistoryShortDateTime.gone() + tvTaskHistoryLongDateTime.visible() + llTaskHistoryActions.visible() + tvTaskHistoryName.maxLines = Integer.MAX_VALUE + tvTaskHistoryName.ellipsize = null + (itemView as CardView).cardElevation = 6f - if (history.done) { - initViewMarkAsNotDone() - } else { - initViewMarkAsDone() - } + if (history.done) { + initViewMarkAsNotDone() } else { - tvTaskHistoryShortDateTime.visible() - tvTaskHistoryLongDateTime.gone() - llTaskHistoryActions.gone() - tvTaskHistoryName.maxLines = 3 - tvTaskHistoryName.ellipsize = TextUtils.TruncateAt.END - (itemView as CardView).cardElevation = 0f + initViewMarkAsDone() } + } else { + tvTaskHistoryShortDateTime.visible() + tvTaskHistoryLongDateTime.gone() + llTaskHistoryActions.gone() + tvTaskHistoryName.maxLines = 3 + tvTaskHistoryName.ellipsize = TextUtils.TruncateAt.END + (itemView as CardView).cardElevation = 0f } + } - private fun initViewMarkAsNotDone() = - with(binding) { - tvTaskHistoryMarkAsDone.gone() - tvTaskHistoryMarkAsNotDone.visible() - tvTaskHistoryMarkAsNotDone.setOnClickListener { callback.onClickMarkTaskAsNotDone(bindingAdapterPosition) } - } + private fun initViewMarkAsNotDone() = with(binding) { + tvTaskHistoryMarkAsDone.gone() + tvTaskHistoryMarkAsNotDone.visible() + tvTaskHistoryMarkAsNotDone.setOnClickListener { callback.onClickMarkTaskAsNotDone(bindingAdapterPosition) } + } - private fun initViewMarkAsDone() = - with(binding) { - tvTaskHistoryMarkAsNotDone.gone() - tvTaskHistoryMarkAsDone.visible() - tvTaskHistoryMarkAsDone.setOnClickListener { callback.onClickMarkTaskAsDone(bindingAdapterPosition) } - } + private fun initViewMarkAsDone() = with(binding) { + tvTaskHistoryMarkAsNotDone.gone() + tvTaskHistoryMarkAsDone.visible() + tvTaskHistoryMarkAsDone.setOnClickListener { callback.onClickMarkTaskAsDone(bindingAdapterPosition) } + } interface Callback { fun onClickMarkTaskAsDone(position: Int) diff --git a/ui-component/impl/src/main/java/br/com/sailboat/uicomponent/impl/viewholder/TaskSkeletonViewHolder.kt b/ui-component/impl/src/main/java/br/com/sailboat/uicomponent/impl/viewholder/TaskSkeletonViewHolder.kt new file mode 100644 index 00000000..56640262 --- /dev/null +++ b/ui-component/impl/src/main/java/br/com/sailboat/uicomponent/impl/viewholder/TaskSkeletonViewHolder.kt @@ -0,0 +1,30 @@ +@file:Suppress("ktlint:standard:indent") + +package br.com.sailboat.uicomponent.impl.viewholder + +import android.view.ViewGroup +import androidx.compose.ui.platform.ComposeView +import androidx.compose.ui.platform.ViewCompositionStrategy +import androidx.recyclerview.widget.RecyclerView +import br.com.sailboat.uicomponent.impl.skeleton.TaskSkeletonItem +import br.com.sailboat.uicomponent.model.TaskSkeletonUiModel + +class TaskSkeletonViewHolder( + parent: ViewGroup, +) : RecyclerView.ViewHolder( + ComposeView(parent.context).apply { + layoutParams = + RecyclerView.LayoutParams( + RecyclerView.LayoutParams.MATCH_PARENT, + RecyclerView.LayoutParams.WRAP_CONTENT, + ) + setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed) + setContent { TaskSkeletonItem() } + }, +) { + fun bind( + @Suppress("UNUSED_PARAMETER") item: TaskSkeletonUiModel, + ) { + // Static skeleton; no-op + } +} diff --git a/ui-component/impl/src/main/java/br/com/sailboat/uicomponent/impl/viewholder/TaskViewHolder.kt b/ui-component/impl/src/main/java/br/com/sailboat/uicomponent/impl/viewholder/TaskViewHolder.kt index b0750876..fe50968d 100644 --- a/ui-component/impl/src/main/java/br/com/sailboat/uicomponent/impl/viewholder/TaskViewHolder.kt +++ b/ui-component/impl/src/main/java/br/com/sailboat/uicomponent/impl/viewholder/TaskViewHolder.kt @@ -38,15 +38,14 @@ class TaskViewHolder(parent: ViewGroup, private val callback: Callback) : private val defaultInlineTopMargin = (binding.task.inlineMetricsContainer.layoutParams as ConstraintLayout.LayoutParams).topMargin - override fun bind(item: TaskUiModel) = - with(binding) { - task.tvTaskName.text = item.taskName - bindTaskAlarm(item) - root.setSafeClickListener { - callback.onClickTask(item.taskId) - } - bindInlineMetrics(item) + override fun bind(item: TaskUiModel) = with(binding) { + task.tvTaskName.text = item.taskName + bindTaskAlarm(item) + root.setSafeClickListener { + callback.onClickTask(item.taskId) } + bindInlineMetrics(item) + } private fun bindTaskAlarm(item: TaskUiModel) { try { @@ -60,72 +59,68 @@ class TaskViewHolder(parent: ViewGroup, private val callback: Callback) : } } - private fun updateAlarmText(alarm: Calendar) = - with(binding) { - if (alarm.isBeforeToday() || alarm.isAfterTomorrow()) { - task.tvTaskDate.text = - if (alarm.isCurrentYear()) { - alarm.getMonthAndDayShort(context) - } else { - alarm.toShortDateView(context) - } - } else { - task.tvTaskTime.text = alarm.formatTimeWithAndroidFormat(context) - } + private fun updateAlarmText(alarm: Calendar) = with(binding) { + if (alarm.isBeforeToday() || alarm.isAfterTomorrow()) { + task.tvTaskDate.text = + if (alarm.isCurrentYear()) { + alarm.getMonthAndDayShort(context) + } else { + alarm.toShortDateView(context) + } + } else { + task.tvTaskTime.text = alarm.formatTimeWithAndroidFormat(context) } + } - private fun updateAlarmColor(alarmColor: Int?) = - with(binding) { - alarmColor?.let { - task.tvTaskDate.setTextColor(alarmColor) - task.tvTaskTime.setTextColor(alarmColor) - } + private fun updateAlarmColor(alarmColor: Int?) = with(binding) { + alarmColor?.let { + task.tvTaskDate.setTextColor(alarmColor) + task.tvTaskTime.setTextColor(alarmColor) } + } - private fun updateVisibilityOfAlarmViews(alarm: Calendar?) = - with(binding) { - if (alarm == null) { - task.tvTaskDate.gone() - task.tvTaskTime.gone() - } else if (alarm.isBeforeToday() || alarm.isAfterTomorrow()) { - task.tvTaskTime.gone() - task.tvTaskDate.visible() - } else { - task.tvTaskTime.visible() - task.tvTaskDate.gone() - } + private fun updateVisibilityOfAlarmViews(alarm: Calendar?) = with(binding) { + if (alarm == null) { + task.tvTaskDate.gone() + task.tvTaskTime.gone() + } else if (alarm.isBeforeToday() || alarm.isAfterTomorrow()) { + task.tvTaskTime.gone() + task.tvTaskDate.visible() + } else { + task.tvTaskTime.visible() + task.tvTaskDate.gone() } + } - private fun bindInlineMetrics(item: TaskUiModel) = - with(binding) { - if (item.showInlineMetrics) { - root.cardElevation = inlineElevation - task.root.minimumHeight = 0 - task.root.setPadding(task.root.paddingLeft, task.root.paddingTop, task.root.paddingRight, 0) - updateInlineTopMargin(0) - task.inlineMetricsContainer.visible() - bindInlineMetricsValues(item) - task.tvTaskName.gone() - task.flTaskDateTime.gone() - task.btnInlineUndo.setSafeClickListener { - callback.onClickUndo(item.taskId, item.inlineStatus ?: TaskStatus.NOT_DONE) - } - } else { - root.cardElevation = defaultElevation - task.root.minimumHeight = defaultMinHeight - task.root.setPadding( - task.root.paddingLeft, - task.root.paddingTop, - task.root.paddingRight, - defaultBottomPadding, - ) - updateInlineTopMargin(defaultInlineTopMargin) - task.inlineMetricsContainer.gone() - task.tvTaskName.visible() - task.flTaskDateTime.visible() - task.btnInlineUndo.setOnClickListener(null) + private fun bindInlineMetrics(item: TaskUiModel) = with(binding) { + if (item.showInlineMetrics) { + root.cardElevation = inlineElevation + task.root.minimumHeight = 0 + task.root.setPadding(task.root.paddingLeft, task.root.paddingTop, task.root.paddingRight, 0) + updateInlineTopMargin(0) + task.inlineMetricsContainer.visible() + bindInlineMetricsValues(item) + task.tvTaskName.gone() + task.flTaskDateTime.gone() + task.btnInlineUndo.setSafeClickListener { + callback.onClickUndo(item.taskId, item.inlineStatus ?: TaskStatus.NOT_DONE) } + } else { + root.cardElevation = defaultElevation + task.root.minimumHeight = defaultMinHeight + task.root.setPadding( + task.root.paddingLeft, + task.root.paddingTop, + task.root.paddingRight, + defaultBottomPadding, + ) + updateInlineTopMargin(defaultInlineTopMargin) + task.inlineMetricsContainer.gone() + task.tvTaskName.visible() + task.flTaskDateTime.visible() + task.btnInlineUndo.setOnClickListener(null) } + } private fun updateInlineTopMargin(margin: Int) { val layoutParams = @@ -134,16 +129,15 @@ class TaskViewHolder(parent: ViewGroup, private val callback: Callback) : binding.task.inlineMetricsContainer.layoutParams = layoutParams } - private fun bindInlineMetricsValues(item: TaskUiModel) = - with(binding.task.inlineTaskMetrics) { - tvMetricsFire.text = item.inlineMetrics?.consecutiveDone?.toString().orEmpty() - tvMetricsDone.text = item.inlineMetrics?.doneTasks?.toString().orEmpty() - tvMetricsNotDone.text = item.inlineMetrics?.notDoneTasks?.toString().orEmpty() + private fun bindInlineMetricsValues(item: TaskUiModel) = with(binding.task.inlineTaskMetrics) { + tvMetricsFire.text = item.inlineMetrics?.consecutiveDone?.toString().orEmpty() + tvMetricsDone.text = item.inlineMetrics?.doneTasks?.toString().orEmpty() + tvMetricsNotDone.text = item.inlineMetrics?.notDoneTasks?.toString().orEmpty() - if ((item.inlineMetrics?.consecutiveDone ?: 0) == 0) { - taskMetricsLlFire.gone() - } else { - taskMetricsLlFire.visible() - } + if ((item.inlineMetrics?.consecutiveDone ?: 0) == 0) { + taskMetricsLlFire.gone() + } else { + taskMetricsLlFire.visible() } + } } diff --git a/ui-component/impl/src/main/java/br/com/sailboat/uicomponent/impl/viewholder/TitleViewHolder.kt b/ui-component/impl/src/main/java/br/com/sailboat/uicomponent/impl/viewholder/TitleViewHolder.kt index c12e9678..216422a2 100644 --- a/ui-component/impl/src/main/java/br/com/sailboat/uicomponent/impl/viewholder/TitleViewHolder.kt +++ b/ui-component/impl/src/main/java/br/com/sailboat/uicomponent/impl/viewholder/TitleViewHolder.kt @@ -9,8 +9,7 @@ class TitleViewHolder(parent: ViewGroup) : BaseViewHolder( VhTitleBinding.inflate(getInflater(parent), parent, false), ) { - override fun bind(item: TitleUiModel) = - with(binding) { - vhTitleTvName.text = item.title - } + override fun bind(item: TitleUiModel) = with(binding) { + vhTitleTvName.text = item.title + } } diff --git a/ui-component/impl/src/main/res/values-pt-rBR/strings.xml b/ui-component/impl/src/main/res/values-pt-rBR/strings.xml index feb34e97..38abf970 100644 --- a/ui-component/impl/src/main/res/values-pt-rBR/strings.xml +++ b/ui-component/impl/src/main/res/values-pt-rBR/strings.xml @@ -66,6 +66,8 @@ Detalhes do Histórico Ocorreu um erro ao efetuar a operação Dias anteriores + Antes de agora + Com alarmes Excluir Salvar --- diff --git a/ui-component/impl/src/main/res/values/strings.xml b/ui-component/impl/src/main/res/values/strings.xml index 8aa74d2d..2ce9c2e9 100644 --- a/ui-component/impl/src/main/res/values/strings.xml +++ b/ui-component/impl/src/main/res/values/strings.xml @@ -16,6 +16,8 @@ History Settings Previous days + Before now + With alarms Mark as done Mark as not done Clear history diff --git a/ui-component/impl/src/test/java/br/com/sailboat/uicomponent/impl/progress/TaskProgressGridBuilderTest.kt b/ui-component/impl/src/test/java/br/com/sailboat/uicomponent/impl/progress/TaskProgressGridBuilderTest.kt index 37e27c14..742bf1d8 100644 --- a/ui-component/impl/src/test/java/br/com/sailboat/uicomponent/impl/progress/TaskProgressGridBuilderTest.kt +++ b/ui-component/impl/src/test/java/br/com/sailboat/uicomponent/impl/progress/TaskProgressGridBuilderTest.kt @@ -44,14 +44,13 @@ internal class TaskProgressGridBuilderTest { private fun createProgressDays( start: LocalDate, count: Int, - ): List = - (0 until count).map { offset -> - val date = start.plusDays(offset.toLong()) - TaskProgressDay( - date = date, - doneCount = 1, - notDoneCount = 0, - totalCount = 1, - ) - } + ): List = (0 until count).map { offset -> + val date = start.plusDays(offset.toLong()) + TaskProgressDay( + date = date, + doneCount = 1, + notDoneCount = 0, + totalCount = 1, + ) + } } diff --git a/ui-component/public/src/main/java/br/com/sailboat/uicomponent/model/TaskSkeletonUiModel.kt b/ui-component/public/src/main/java/br/com/sailboat/uicomponent/model/TaskSkeletonUiModel.kt new file mode 100644 index 00000000..5e1c03cd --- /dev/null +++ b/ui-component/public/src/main/java/br/com/sailboat/uicomponent/model/TaskSkeletonUiModel.kt @@ -0,0 +1,6 @@ +package br.com.sailboat.uicomponent.model + +data class TaskSkeletonUiModel( + val placeholderId: Long, + override val uiModelId: Int = UiModelType.TASK_SKELETON.ordinal, +) : UiModel diff --git a/ui-component/public/src/main/java/br/com/sailboat/uicomponent/model/UiModelType.kt b/ui-component/public/src/main/java/br/com/sailboat/uicomponent/model/UiModelType.kt index 3817cb20..9a8060b2 100644 --- a/ui-component/public/src/main/java/br/com/sailboat/uicomponent/model/UiModelType.kt +++ b/ui-component/public/src/main/java/br/com/sailboat/uicomponent/model/UiModelType.kt @@ -5,6 +5,7 @@ enum class UiModelType { TASK_HISTORY, ALARM, SUBHEADER, + TASK_SKELETON, EMPTY_LIST_TASK, DAY, TITLE,