Skip to content

Commit

Permalink
✨ Add Creation and Completed date on Tasks
Browse files Browse the repository at this point in the history
Adding a new feature to save the creation date and when the user completed the task. This will allow the "Tracker" feature to be possible, once it will show how many tasks were finished during a timestamp.
Test cases added to validate new behavior.
  • Loading branch information
igorescodro authored Sep 16, 2019
2 parents 6041315 + 50aabe6 commit 38dd53f
Show file tree
Hide file tree
Showing 32 changed files with 710 additions and 18 deletions.
2 changes: 2 additions & 0 deletions buildSrc/src/main/java/Dependencies.kt
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ object Versions {
const val testEspressoContrib = "3.1.0-alpha4"
const val testUiAutomator = "2.2.0"
const val testJunitExt = "1.1.0"
const val testRoom = "2.1.0"

const val buildGradle = "3.5.0"

Expand Down Expand Up @@ -101,6 +102,7 @@ object TestDeps {
val uiAutomator = "androidx.test.uiautomator:uiautomator:${Versions.testUiAutomator}"
val junitExt = "androidx.test.ext:junit:${Versions.testJunitExt}"
val mockk = "io.mockk:mockk:${Versions.testMockk}"
val room = "androidx.room:room-testing:${Versions.testRoom}"
val espresso = EspressoDeps
}

Expand Down
5 changes: 5 additions & 0 deletions data/domain/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,12 @@ dependencies {
implementation project(":data:local")
implementation project(":data:model")

testImplementation project(':libraries:test')

implementation Deps.koin.core
implementation Deps.rx.java
implementation Deps.rx.android

testImplementation Deps.test.junit
testImplementation Deps.test.mockk
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.escodro.domain.calendar

import java.util.Calendar

/**
* Provide the date and time to be used on the task use cases, respecting the Inversion of Control.
*/
class TaskCalendar {

/**
* Gets the current [Calendar].
*
* @return the current [Calendar]
*/
fun getCurrentCalendar(): Calendar = Calendar.getInstance()
}
11 changes: 9 additions & 2 deletions data/domain/src/main/java/com/escodro/domain/di/DomainModule.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.escodro.domain.di

import com.escodro.domain.calendar.TaskCalendar
import com.escodro.domain.mapper.CategoryMapper
import com.escodro.domain.mapper.TaskMapper
import com.escodro.domain.mapper.TaskWithCategoryMapper
Expand All @@ -13,7 +14,9 @@ import com.escodro.domain.usecase.task.DeleteTask
import com.escodro.domain.usecase.task.GetFutureTasks
import com.escodro.domain.usecase.task.GetTask
import com.escodro.domain.usecase.task.SnoozeTask
import com.escodro.domain.usecase.task.UncompleteTask
import com.escodro.domain.usecase.task.UpdateTask
import com.escodro.domain.usecase.task.UpdateTaskStatus
import com.escodro.domain.usecase.taskwithcategory.LoadCompletedTasks
import com.escodro.domain.usecase.taskwithcategory.LoadTasksByCategory
import com.escodro.domain.usecase.taskwithcategory.LoadUncompletedTasks
Expand All @@ -23,8 +26,10 @@ import org.koin.dsl.module
* Domain dependency injection module.
*/
val domainModule = module {
single { AddTask(get(), get()) }
single { CompleteTask(get(), get()) }
single { AddTask(get(), get(), get()) }
single { CompleteTask(get(), get(), get()) }
single { UncompleteTask(get(), get()) }
single { UpdateTaskStatus(get(), get()) }
single { DeleteTask(get(), get()) }
single { GetFutureTasks(get(), get()) }
single { GetTask(get(), get()) }
Expand All @@ -43,4 +48,6 @@ val domainModule = module {
single { CategoryMapper() }
single { TaskMapper() }
single { TaskWithCategoryMapper(get(), get()) }

single { TaskCalendar() }
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@ class TaskMapper {
title = task.title,
description = task.description,
categoryId = task.categoryId,
dueDate = task.dueDate
dueDate = task.dueDate,
creationDate = task.creationDate,
completedDate = task.completedDate
)

/**
Expand All @@ -39,6 +41,8 @@ class TaskMapper {
title = task.title,
description = task.description,
categoryId = task.categoryId,
dueDate = task.dueDate
dueDate = task.dueDate,
creationDate = task.creationDate,
completedDate = task.completedDate
)
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.escodro.domain.usecase.task

import com.escodro.core.extension.applySchedulers
import com.escodro.domain.calendar.TaskCalendar
import com.escodro.domain.mapper.TaskMapper
import com.escodro.domain.viewdata.ViewData
import com.escodro.local.provider.DaoProvider
Expand All @@ -9,7 +10,11 @@ import io.reactivex.Completable
/**
* Use case to add a task from the database.
*/
class AddTask(private val daoProvider: DaoProvider, private val mapper: TaskMapper) {
class AddTask(
private val daoProvider: DaoProvider,
private val mapper: TaskMapper,
private val taskCalendar: TaskCalendar
) {

/**
* Adds a task.
Expand All @@ -20,6 +25,7 @@ class AddTask(private val daoProvider: DaoProvider, private val mapper: TaskMapp
*/
operator fun invoke(task: ViewData.Task): Completable {
val entityTask = mapper.toEntityTask(task)
entityTask.creationDate = taskCalendar.getCurrentCalendar()
return daoProvider.getTaskDao().insertTask(entityTask).applySchedulers()
}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
package com.escodro.domain.usecase.task

import com.escodro.core.extension.applySchedulers
import com.escodro.domain.calendar.TaskCalendar
import io.reactivex.Completable

/**
* Use case to set a task as completed in the database.
*/
class CompleteTask(private val getTask: GetTask, private val updateTask: UpdateTask) {
class CompleteTask(
private val getTask: GetTask,
private val updateTask: UpdateTask,
private val taskCalendar: TaskCalendar
) {

/**
* Completes the given task.
Expand All @@ -18,6 +23,7 @@ class CompleteTask(private val getTask: GetTask, private val updateTask: UpdateT
operator fun invoke(taskId: Long): Completable =
getTask(taskId).flatMapCompletable {
it.completed = true
it.completedDate = taskCalendar.getCurrentCalendar()
updateTask(it)
}.applySchedulers()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.escodro.domain.usecase.task

import com.escodro.core.extension.applySchedulers
import io.reactivex.Completable

/**
* Use case to set a task as uncompleted in the database.
*/
class UncompleteTask(private val getTask: GetTask, private val updateTask: UpdateTask) {

/**
* Completes the given task.
*
* @param taskId the task id
*
* @return observable to be subscribe
*/
operator fun invoke(taskId: Long): Completable =
getTask(taskId).flatMapCompletable {
it.completed = false
it.completedDate = null
updateTask(it)
}.applySchedulers()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.escodro.domain.usecase.task

import com.escodro.core.extension.applySchedulers
import com.escodro.domain.viewdata.ViewData
import io.reactivex.Completable

/**
* Use case to update a task as completed or uncompleted from the database.
*/
class UpdateTaskStatus(
private val completeTask: CompleteTask,
private val uncompleteTask: UncompleteTask
) {

/**
* Updates the task as completed or uncompleted based on the current state.
*
* @param task the task to be updated
*
* @return observable to be subscribe
*/
operator fun invoke(task: ViewData.Task): Completable {
task.completed = !task.completed

return when (task.completed) {
true -> completeTask(task.id)
false -> uncompleteTask(task.id)
}.applySchedulers()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ sealed class ViewData {
var title: String,
var description: String? = null,
var categoryId: Long? = null,
var dueDate: Calendar? = null
var dueDate: Calendar? = null,
var creationDate: Calendar? = null,
var completedDate: Calendar? = null
) : Parcelable

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package com.escodro.domain.usecase.task

import com.escodro.domain.calendar.TaskCalendar
import com.escodro.domain.viewdata.ViewData
import com.escodro.test.ImmediateSchedulerRule
import io.mockk.every
import io.mockk.mockk
import io.mockk.verify
import io.reactivex.Single
import java.util.Calendar
import org.junit.Rule
import org.junit.Test

class CompleteTaskTest {

@get:Rule
var testSchedulerRule = ImmediateSchedulerRule()

private val mockTask = mockk<ViewData.Task>(relaxed = true)

private val mockGetTask = mockk<GetTask>(relaxed = true)

private val mockUpdateTask = mockk<UpdateTask>(relaxed = true)

private val mockCalendar = mockk<TaskCalendar>(relaxed = true)

private val completeTask = CompleteTask(mockGetTask, mockUpdateTask, mockCalendar)

@Test
fun `check if task was completed`() {
val currentTime = Calendar.getInstance()

every { mockCalendar.getCurrentCalendar() } returns currentTime
every { mockGetTask.invoke(any()) } returns Single.just(mockTask)

completeTask(mockTask.id).subscribe()

verify { mockTask.completed = true }
verify { mockTask.completedDate = currentTime }
verify { mockUpdateTask.invoke(any()) }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package com.escodro.domain.usecase.task

import com.escodro.domain.viewdata.ViewData
import com.escodro.test.ImmediateSchedulerRule
import io.mockk.every
import io.mockk.mockk
import io.mockk.verify
import io.reactivex.Single
import org.junit.Rule
import org.junit.Test

class UncompleteTaskTest {

@get:Rule
var testSchedulerRule = ImmediateSchedulerRule()

private val mockTask = mockk<ViewData.Task>(relaxed = true)

private val mockGetTask = mockk<GetTask>(relaxed = true)

private val mockUpdateTask = mockk<UpdateTask>(relaxed = true)

private val uncompleteTask = UncompleteTask(mockGetTask, mockUpdateTask)

@Test
fun `check if task was uncompleted`() {
every { mockGetTask.invoke(any()) } returns Single.just(mockTask)

uncompleteTask(mockTask.id).subscribe()

verify { mockTask.completed = false }
verify { mockTask.completedDate = null }
verify { mockUpdateTask.invoke(any()) }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package com.escodro.domain.usecase.task

import com.escodro.domain.viewdata.ViewData
import com.escodro.test.ImmediateSchedulerRule
import io.mockk.every
import io.mockk.mockk
import io.mockk.verify
import org.junit.Rule
import org.junit.Test

class UpdateTaskStatusTest {

@get:Rule
var testSchedulerRule = ImmediateSchedulerRule()

private val mockTask = mockk<ViewData.Task>(relaxed = true)

private val mockCompleteTask = mockk<CompleteTask>(relaxed = true)

private val mockUncompleteTask = mockk<UncompleteTask>(relaxed = true)

private val updateTaskStatus = UpdateTaskStatus(mockCompleteTask, mockUncompleteTask)

@Test
fun `check if completed flag was inverted`() {
updateTaskStatus(mockTask)
verify { mockTask.completed = !mockTask.completed }
}

@Test
fun `check if completed flow was called`() {
every { mockTask.completed } returns false andThen true

updateTaskStatus(mockTask)
verify { mockTask.completed = true }
verify { mockCompleteTask(mockTask.id) }
}

@Test
fun `check if uncompleted flow was called`() {
every { mockTask.completed } returns true andThen false

updateTaskStatus(mockTask)
verify { mockTask.completed = false }
verify { mockUncompleteTask(mockTask.id) }
}
}
16 changes: 16 additions & 0 deletions data/local/build.gradle
Original file line number Diff line number Diff line change
@@ -1,6 +1,19 @@
apply from: "$rootDir/config/dependencies/feature_dependencies.gradle"
apply plugin: "kotlin-kapt"

android {
defaultConfig {
javaCompileOptions {
annotationProcessorOptions {
arguments = ["room.schemaLocation": "$projectDir/schemas".toString()]
}
}
}
sourceSets {
androidTest.assets.srcDirs += files("$projectDir/schemas".toString())
}
}

dependencies {
implementation project(":libraries:core")
implementation project(":data:model")
Expand All @@ -10,4 +23,7 @@ dependencies {
implementation Deps.android.room.rx
kapt Deps.android.room.compiler
implementation Deps.rx.java

androidTestImplementation Deps.test.runner
androidTestImplementation Deps.test.room
}
Loading

0 comments on commit 38dd53f

Please sign in to comment.