Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ kotlin {

val androidInstrumentedTest by getting {
dependencies {
implementation(libs.espresso.core)
implementation(libs.androidx.test.runner)
implementation(libs.androidx.test.ext.junit)
implementation(libs.hilt.android.testing)
implementation(libs.mockito.android)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,22 +1,36 @@
package net.cacheux.nvp.app

import androidx.activity.ComponentActivity
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.assertIsEnabled
import androidx.compose.ui.test.assertIsNotDisplayed
import androidx.compose.ui.test.assertIsNotEnabled
import androidx.compose.ui.test.isDisplayed
import androidx.compose.ui.test.isNotDisplayed
import androidx.compose.ui.test.junit4.AndroidComposeTestRule
import androidx.compose.ui.test.junit4.createAndroidComposeRule
import androidx.compose.ui.test.longClick
import androidx.compose.ui.test.onNodeWithContentDescription
import androidx.compose.ui.test.onNodeWithTag
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performClick
import androidx.compose.ui.test.performTouchInput
import androidx.test.ext.junit.runners.AndroidJUnit4
import dagger.hilt.android.testing.HiltAndroidRule
import dagger.hilt.android.testing.HiltAndroidTest
import dagger.hilt.android.testing.UninstallModules
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.runBlocking
import net.cacheux.nvp.app.repository.PenInfoRepository
import net.cacheux.nvp.ui.ui.generated.resources.Res
import net.cacheux.nvp.ui.ui.generated.resources.delete_dose_warning
import net.cacheux.nvp.ui.ui.generated.resources.delete_selected
import net.cacheux.nvp.ui.ui.generated.resources.ok
import net.cacheux.nvp.ui.ui.generated.resources.open_drawer
import net.cacheux.nvp.ui.ui.generated.resources.reading_pen
import net.cacheux.nvplib.storage.DoseStorage
import org.jetbrains.compose.resources.getString
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Before
Expand All @@ -31,6 +45,9 @@ import javax.inject.Inject
@RunWith(AndroidJUnit4::class)
class MainScreenTest {

@Inject
lateinit var doseStorage: DoseStorage

@Inject
lateinit var penInfoRepository: PenInfoRepository

Expand Down Expand Up @@ -85,6 +102,63 @@ class MainScreenTest {
assertTrue(activity.isFinishing)
}
}

@Test
fun testDoseDeletion() = runBlocking {
composeTestRule.run {
doseStorage.insertData()

assertEquals(9, doseStorage.getAllDoses().first().size)

waitForIdle()

onNodeWithText("52.0").performClick()
waitForIdle()

onNodeWithText("2.0").isDisplayed()

onNodeWithTag("doseGroupDetails", useUnmergedTree = true).performTouchInput {
longClick()
}
waitForIdle()

onNodeWithText(getString(Res.string.delete_selected))
.assertIsDisplayed()
.assertIsNotEnabled()

onNodeWithTag("doseCheck8").assertIsDisplayed()
onNodeWithTag("doseCheck9").assertIsDisplayed()
.performClick()
waitForIdle()

onNodeWithText(getString(Res.string.delete_selected))
.assertIsDisplayed()
.assertIsEnabled()
.performClick()
waitForIdle()

onNodeWithText(getString(Res.string.delete_dose_warning))
.assertIsDisplayed()

onNodeWithText(getString(Res.string.ok))
.performClick()
waitForIdle()

onNodeWithText(getString(Res.string.delete_dose_warning))
.assertIsNotDisplayed()

onNodeWithTag("doseCheck8").assertExists()
onNodeWithTag("doseCheck9").assertDoesNotExist()

onNodeWithText(getString(Res.string.delete_selected))
.assertIsDisplayed()
.assertIsNotEnabled()

onNodeWithText("52.0").assertDoesNotExist()

assertEquals(8, doseStorage.getAllDoses().first().size)
}
}
}

fun <R: TestRule, A: ComponentActivity> AndroidComposeTestRule<R, A>.pressBack() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import dagger.hilt.android.testing.HiltAndroidRule
import dagger.hilt.android.testing.HiltAndroidTest
import dagger.hilt.android.testing.UninstallModules
import jakarta.inject.Inject
import kotlinx.coroutines.runBlocking
import net.cacheux.nvp.ui.ui.generated.resources.Res
import net.cacheux.nvp.ui.ui.generated.resources.auto_ignore
Expand All @@ -33,6 +32,7 @@ import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import javax.inject.Inject

@HiltAndroidTest
@UninstallModules(NvpModule::class)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ class StorageRepository(
storage.deletePen(serial)
}

suspend fun deleteDose(id: Long) {
storage.deleteDose(id)
}

override suspend fun getStopCondition(): StopCondition {
val currentTime = System.currentTimeMillis()
val stopMap = storage.listAllPens().first().map {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,9 @@ fun ScreenWrapper(
message = mainScreenViewModel.getReadMessage().collectAsState().value?.let {
stringResource(it)
},
onDismissMessage = { mainScreenViewModel.clearPopup() },
onDismissMessage = mainScreenViewModel::clearPopup,

onDoseDeletion = mainScreenViewModel::deleteDoses,

dropdownMenuParams = MainDropdownMenuParams(
loadingFileAvailable = true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,14 @@ open class BaseMainScreenViewModel (

val store = repository.getDataStore()

fun deleteDoses(doses: List<Dose>) {
coroutineScope.launch {
doses.forEach {
storageRepository.deleteDose(it.id)
}
}
}

fun loadCsvFile(input: InputStream) {
input.reader().use {
it.readText().csvToDoseList().let { doseList ->
Expand Down
16 changes: 8 additions & 8 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@ java = "17"
android-minSdk = "24"
android-compileSdk = "36"
agp = "8.13.0"
compose-plugin="1.8.2"
compose-plugin="1.9.0"
hilt = "2.57.1"
kotlin = "2.1.21"
coroutines = "1.10.2"
room = "2.7.2"
sqlite = "2.5.2"
room = "2.8.2"
sqlite = "2.6.1"
androidx-datastore = "1.1.7"

[plugins]
Expand All @@ -32,7 +32,7 @@ kotlin-stdlib = { module = "org.jetbrains.kotlin:kotlin-stdlib", version.ref = "
kotlinx-coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "coroutines" }
kotlinx-coroutines-swing = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-swing", version.ref = "coroutines" }
kotlinx-coroutines-android = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android", version.ref = "coroutines" }
androidx-activity-compose = { module = "androidx.activity:activity-compose", version = "1.10.1" }
androidx-activity-compose = { module = "androidx.activity:activity-compose", version = "1.11.0" }
androidx-datastore-preferences = { module = "androidx.datastore:datastore-preferences", version = "androidx-datastore" }
androidx-datastore-core = { module = "androidx.datastore:datastore-core", version.ref = "androidx-datastore" }
androidx-lifecycle-viewmodel-compose = { module = "org.jetbrains.androidx.lifecycle:lifecycle-viewmodel-compose", version = "2.9.3" }
Expand All @@ -51,8 +51,8 @@ hilt-android-testing = { module = "com.google.dagger:hilt-android-testing", vers
# Testing
junit = { group = "junit", name = "junit", version = "4.13.2" }
androidx-test-ext-junit = { group = "androidx.test.ext", name = "junit", version = "1.3.0" }
espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version = "3.7.0" }
mockito-android = { group = "org.mockito", name = "mockito-android", version = "5.19.0" }
mockito-kotlin = { group = "org.mockito.kotlin", name = "mockito-kotlin", version = "6.0.0" }
androidx-test-runner = { group = "androidx.test", name = "runner", version="1.7.0" }
mockito-android = { group = "org.mockito", name = "mockito-android", version = "5.20.0" }
mockito-kotlin = { group = "org.mockito.kotlin", name = "mockito-kotlin", version = "6.1.0" }
turbine = { group = "app.cash.turbine", name = "turbine", version = "1.2.1" }
androidx-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling", version = "1.9.0" }
androidx-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling", version = "1.9.3" }
5 changes: 3 additions & 2 deletions model/src/commonMain/kotlin/net/cacheux/nvp/model/Dose.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@ data class Dose(
val value: Int,
val ignored: Boolean = false,
val serial: String = "",
val color: String = ""
val color: String = "",
val id: Long = 0
): DatedItem {
fun ignored() = Dose(time, value, true, serial, color)
fun ignored() = Dose(time, value, true, serial, color, id)

fun displayedValue() = String.format("%.1f", value.toFloat() / 10)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ class DoseGroup(
fun displayedTotal() = String.format("%.1f", getTotal().toFloat() / 10)

override fun date() = timestampToDate(getTime())

fun containsSameDosesAs(group: DoseGroup): Boolean {
return doses.any { group.doses.map { it.id }.contains(it.id) }
}
}

fun List<Dose>.toDoseListWithIgnoredFlag(config: DoseGroupConfig): List<Dose> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,10 @@ class RoomDoseStorage(
database.doseDao().deletePen(serial)
}

override suspend fun deleteDose(id: Long) {
database.doseDao().deleteDoseById(id)
}

override fun listAllPens(): Flow<List<PenInfos>>
= database.doseDao().listAllPens().map { it.map { pen -> pen.toPenInfos() } }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@ interface DoseDao {
@Query("DELETE FROM pen WHERE serial = :serial")
suspend fun deletePenBySerial(serial: String)

@Query("DELETE FROM dose WHERE id = :id")
suspend fun deleteDoseById(id: Long)

@Query("DELETE FROM dose")
suspend fun deleteAllDoses()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ data class DoseWithPen(
time = dose.time,
value = dose.value,
serial = pen.serial,
color = pen.color
color = pen.color,
id = dose.id
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ data class PenWithDoses(
time = it.time,
value = it.value,
serial = roomPen.serial,
color = roomPen.color
color = roomPen.color,
id = it.id
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,12 @@ data class RoomDose(
fun Dose.toRoomDose(penId: Long = 0) = RoomDose(
time = time,
value = value,
pen = penId
pen = penId,
id = id
)

fun RoomDose.toDose() = Dose(
time = time,
value = value
value = value,
id = id
)
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,21 @@ class RoomDoseStorageTest {
assertEquals(12346850L, storage.getLastDose("ABCD2345").first()?.time)
}

@Test
fun testDeleteDoses() = runBlocking {
val storage = initStorage().apply { createDataset() }

assertEquals(5, storage.getAllDoses().first().size)

storage.deleteDose(3)
storage.deleteDose(4)

assertEquals(3, storage.getAllDoses().first().size)
assertEquals(12345678L, storage.getAllDoses().first()[2].time)
assertEquals(12345690L, storage.getAllDoses().first()[1].time)
assertEquals(12346850L, storage.getAllDoses().first()[0].time)
}

private fun initStorage(): RoomDoseStorage
= RoomDoseStorage(
Room.inMemoryDatabaseBuilder<NvpDatabase>()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,7 @@ interface DoseStorage {

suspend fun deletePen(serial: String)

suspend fun deleteDose(id: Long)

fun listAllPens(): Flow<List<PenInfos>>
}
3 changes: 3 additions & 0 deletions ui/src/commonMain/composeResources/values-fr/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@
<string name="reset">Réinitialiser</string>
<string name="clear">Supprimer</string>

<string name="delete_selected">Supprimer la sélection</string>
<string name="delete_dose_warning">Êtes vous sûr de vouloir supprimer les doses sélectionnées ?</string>

<!-- Pen settings screen -->
<string name="pen_name">Nom</string>
<string name="pen_color">Couleur</string>
Expand Down
3 changes: 3 additions & 0 deletions ui/src/commonMain/composeResources/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@
<string name="reset">Reset</string>
<string name="clear">Clear</string>

<string name="delete_selected">Delete selected</string>
<string name="delete_dose_warning">Are you sure you want to delete the selected doses?</string>

<!-- Pen settings screen -->
<string name="pen_name">Name</string>
<string name="pen_color">Color</string>
Expand Down
30 changes: 6 additions & 24 deletions ui/src/commonMain/kotlin/net/cacheux/nvp/ui/ActionPreference.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,8 @@ import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
Expand All @@ -18,10 +16,6 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.unit.dp
import net.cacheux.nvp.ui.ui.generated.resources.Res
import net.cacheux.nvp.ui.ui.generated.resources.cancel
import net.cacheux.nvp.ui.ui.generated.resources.ok
import org.jetbrains.compose.resources.stringResource

@Composable
fun ActionPreference(
Expand Down Expand Up @@ -51,24 +45,12 @@ fun ActionPreference(
Spacer(modifier = Modifier.weight(1f))

if (showDialog) {
AlertDialog(
onDismissRequest = { showDialog = false },
title = { Text(text = label) },
text = { Text(text = confirmMessage ?: "") },
confirmButton = {
TextButton(onClick = {
action()
showDialog = false
}) {
Text(stringResource(Res.string.ok))
}
},
dismissButton = {
TextButton(onClick = { showDialog = false }) {
Text(stringResource(Res.string.cancel))
}
}
ConfirmDialog(
label = label,
confirmMessage = confirmMessage,
action = action,
onDismiss = { showDialog = false }
)
}
}
}
}
Loading