From 7449a1db82550062a378ee1410d4907b9c42b48a Mon Sep 17 00:00:00 2001 From: Vibhor Goel Date: Fri, 17 Jul 2026 13:28:18 +0530 Subject: [PATCH] test: cover critical regression paths --- .../common/OverlayMenuAnimationsTests.kt | 61 +++++++++++ .../ScenarioDataSourceTransactionTests.kt | 97 +++++++++++++++++ .../ui/EditionRepositoryTestFixtures.kt | 38 +++++++ .../ui/action/ActionTextViewModelTests.kt | 63 +++++++++++ .../number/NumberConditionViewModelTests.kt | 84 +++++++++++++++ .../ui/event/EventDialogViewModelTests.kt | 101 ++++++++++++++++++ .../scenario/ScenarioDialogViewModelTests.kt | 46 ++++++++ .../TriggerEventListViewModelTests.kt | 73 +++++++++++++ 8 files changed, 563 insertions(+) create mode 100644 core/common/overlays/src/test/java/com/buzbuz/smartautoclicker/core/common/overlays/menu/implementation/common/OverlayMenuAnimationsTests.kt create mode 100644 core/smart/domain/src/test/java/com/buzbuz/smartautoclicker/core/domain/data/ScenarioDataSourceTransactionTests.kt create mode 100644 feature/smart-config/src/test/java/com/buzbuz/smartautoclicker/feature/smart/config/ui/EditionRepositoryTestFixtures.kt create mode 100644 feature/smart-config/src/test/java/com/buzbuz/smartautoclicker/feature/smart/config/ui/action/ActionTextViewModelTests.kt create mode 100644 feature/smart-config/src/test/java/com/buzbuz/smartautoclicker/feature/smart/config/ui/condition/screen/number/NumberConditionViewModelTests.kt create mode 100644 feature/smart-config/src/test/java/com/buzbuz/smartautoclicker/feature/smart/config/ui/event/EventDialogViewModelTests.kt create mode 100644 feature/smart-config/src/test/java/com/buzbuz/smartautoclicker/feature/smart/config/ui/scenario/ScenarioDialogViewModelTests.kt create mode 100644 feature/smart-config/src/test/java/com/buzbuz/smartautoclicker/feature/smart/config/ui/scenario/triggerevents/TriggerEventListViewModelTests.kt diff --git a/core/common/overlays/src/test/java/com/buzbuz/smartautoclicker/core/common/overlays/menu/implementation/common/OverlayMenuAnimationsTests.kt b/core/common/overlays/src/test/java/com/buzbuz/smartautoclicker/core/common/overlays/menu/implementation/common/OverlayMenuAnimationsTests.kt new file mode 100644 index 000000000..4256807ab --- /dev/null +++ b/core/common/overlays/src/test/java/com/buzbuz/smartautoclicker/core/common/overlays/menu/implementation/common/OverlayMenuAnimationsTests.kt @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2026 Kevin Buzeau + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + */ +package com.buzbuz.smartautoclicker.core.common.overlays.menu.implementation.common + +import android.os.Build +import android.view.View + +import androidx.test.core.app.ApplicationProvider + +import org.junit.Assert.assertEquals +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner +import org.robolectric.Shadows.shadowOf +import org.robolectric.annotation.Config + +import java.util.concurrent.TimeUnit + +/** Ensures an animation completion cancels its delayed fallback callback. */ +@RunWith(RobolectricTestRunner::class) +@Config(sdk = [Build.VERSION_CODES.Q]) +class OverlayMenuAnimationsTests { + + @Test + fun showAnimation_completionDoesNotAlsoRunDelayedTimeout() { + val animations = OverlayMenuAnimations() + val view = View(ApplicationProvider.getApplicationContext()) + var completionCount = 0 + + animations.startShowAnimation(view) { completionCount++ } + + shadowOf(android.os.Looper.getMainLooper()).idleFor(300, TimeUnit.MILLISECONDS) + assertEquals(1, completionCount) + + shadowOf(android.os.Looper.getMainLooper()).idleFor(1, TimeUnit.SECONDS) + + assertEquals(1, completionCount) + } + + @Test + fun hideAnimation_completionDoesNotAlsoRunDelayedTimeout() { + val animations = OverlayMenuAnimations() + val view = View(ApplicationProvider.getApplicationContext()) + var completionCount = 0 + + animations.startHideAnimation(view) { completionCount++ } + + shadowOf(android.os.Looper.getMainLooper()).idleFor(200, TimeUnit.MILLISECONDS) + assertEquals(1, completionCount) + + shadowOf(android.os.Looper.getMainLooper()).idleFor(1, TimeUnit.SECONDS) + + assertEquals(1, completionCount) + } +} diff --git a/core/smart/domain/src/test/java/com/buzbuz/smartautoclicker/core/domain/data/ScenarioDataSourceTransactionTests.kt b/core/smart/domain/src/test/java/com/buzbuz/smartautoclicker/core/domain/data/ScenarioDataSourceTransactionTests.kt new file mode 100644 index 000000000..67766a501 --- /dev/null +++ b/core/smart/domain/src/test/java/com/buzbuz/smartautoclicker/core/domain/data/ScenarioDataSourceTransactionTests.kt @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2026 Kevin Buzeau + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + */ +package com.buzbuz.smartautoclicker.core.domain.data + +import android.content.Context +import android.os.Build + +import androidx.room.Room +import androidx.test.core.app.ApplicationProvider + +import com.buzbuz.smartautoclicker.core.base.identifier.Identifier +import com.buzbuz.smartautoclicker.core.database.ClickDatabase +import com.buzbuz.smartautoclicker.core.domain.model.scenario.ScenarioTestsData + +import kotlinx.coroutines.test.runTest + +import org.junit.After +import org.junit.Assert.assertEquals +import org.junit.Assert.assertNotNull +import org.junit.Assert.assertTrue +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner +import org.robolectric.annotation.Config + +/** Ensures external image cleanup only runs after a scenario transaction commits. */ +@RunWith(RobolectricTestRunner::class) +@Config(sdk = [Build.VERSION_CODES.Q]) +class ScenarioDataSourceTransactionTests { + + private lateinit var database: ClickDatabase + private lateinit var dataSource: ScenarioDataSource + + @Before + fun setUp() { + database = Room.inMemoryDatabaseBuilder( + ApplicationProvider.getApplicationContext(), + ClickDatabase::class.java, + ).allowMainThreadQueries().build() + dataSource = ScenarioDataSource(database) + } + + @After + fun tearDown() { + database.close() + } + + @Test + fun addCompleteScenario_cleanupCallbackRunsAfterScenarioIsCommitted() = runTest { + var callbackObservedCommittedScenario = false + + val insertedId = dataSource.addCompleteScenario( + scenario = ScenarioTestsData.getNewScenario(id = 0), + events = emptyList(), + counters = emptyList(), + onImageConditionsRemoved = { + callbackObservedCommittedScenario = database.scenarioDao().getScenario(1L) != null + }, + ) + + assertEquals(1L, insertedId) + assertNotNull(database.scenarioDao().getScenario(1L)) + assertEquals(true, callbackObservedCommittedScenario) + } + + @Test + fun updateScenario_cleanupCallbackRunsAfterScenarioIsCommitted() = runTest { + val scenario = ScenarioTestsData.getNewScenario(id = 0) + val scenarioId = dataSource.addCompleteScenario( + scenario = scenario, + events = emptyList(), + counters = emptyList(), + onImageConditionsRemoved = {}, + )!! + var callbackObservedUpdatedScenario = false + + val result = dataSource.updateScenario( + scenario = scenario.copy(id = Identifier(databaseId = scenarioId), name = "Updated scenario"), + events = emptyList(), + counters = emptyList(), + onImageConditionsRemoved = { + callbackObservedUpdatedScenario = database.scenarioDao().getScenario(scenarioId)?.scenario?.name == "Updated scenario" + }, + ) + + assertTrue(result) + assertEquals("Updated scenario", database.scenarioDao().getScenario(scenarioId)?.scenario?.name) + assertTrue(callbackObservedUpdatedScenario) + } +} diff --git a/feature/smart-config/src/test/java/com/buzbuz/smartautoclicker/feature/smart/config/ui/EditionRepositoryTestFixtures.kt b/feature/smart-config/src/test/java/com/buzbuz/smartautoclicker/feature/smart/config/ui/EditionRepositoryTestFixtures.kt new file mode 100644 index 000000000..a04ae4eb6 --- /dev/null +++ b/feature/smart-config/src/test/java/com/buzbuz/smartautoclicker/feature/smart/config/ui/EditionRepositoryTestFixtures.kt @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2026 Kevin Buzeau + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + */ +package com.buzbuz.smartautoclicker.feature.smart.config.ui + +import com.buzbuz.smartautoclicker.core.bitmaps.BitmapRepository +import com.buzbuz.smartautoclicker.core.domain.IRepository +import com.buzbuz.smartautoclicker.core.domain.model.counter.Counter +import com.buzbuz.smartautoclicker.core.domain.model.event.ScreenEvent +import com.buzbuz.smartautoclicker.core.domain.model.event.TriggerEvent +import com.buzbuz.smartautoclicker.core.domain.model.scenario.Scenario +import com.buzbuz.smartautoclicker.feature.smart.config.domain.EditionRepository + +import io.mockk.coEvery +import io.mockk.mockk + +internal suspend fun createEditionRepository( + scenario: Scenario, + screenEvents: List = emptyList(), + triggerEvents: List = emptyList(), + counters: List = emptyList(), +): EditionRepository { + val repository = mockk { + coEvery { getScenario(scenario.id.databaseId) } returns scenario + coEvery { getScreenEvents(scenario.id.databaseId) } returns screenEvents + coEvery { getTriggerEvents(scenario.id.databaseId) } returns triggerEvents + coEvery { getCounters(scenario.id.databaseId) } returns counters + } + + return EditionRepository(repository, mockk(relaxed = true)).also { + check(it.startEdition(scenario.id.databaseId)) + } +} diff --git a/feature/smart-config/src/test/java/com/buzbuz/smartautoclicker/feature/smart/config/ui/action/ActionTextViewModelTests.kt b/feature/smart-config/src/test/java/com/buzbuz/smartautoclicker/feature/smart/config/ui/action/ActionTextViewModelTests.kt new file mode 100644 index 000000000..23018ba24 --- /dev/null +++ b/feature/smart-config/src/test/java/com/buzbuz/smartautoclicker/feature/smart/config/ui/action/ActionTextViewModelTests.kt @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2026 Kevin Buzeau + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + */ +package com.buzbuz.smartautoclicker.feature.smart.config.ui.action + +import android.os.Build + +import com.buzbuz.smartautoclicker.core.base.identifier.Identifier +import com.buzbuz.smartautoclicker.core.common.actions.text.appendCounterReference +import com.buzbuz.smartautoclicker.core.domain.model.AND +import com.buzbuz.smartautoclicker.core.domain.model.action.Notification +import com.buzbuz.smartautoclicker.core.domain.model.action.SetText +import com.buzbuz.smartautoclicker.core.domain.model.event.ScreenEvent +import com.buzbuz.smartautoclicker.core.domain.model.scenario.Scenario +import com.buzbuz.smartautoclicker.feature.smart.config.ui.action.notification.NotificationViewModel +import com.buzbuz.smartautoclicker.feature.smart.config.ui.action.settext.SetTextViewModel +import com.buzbuz.smartautoclicker.feature.smart.config.ui.createEditionRepository + +import kotlinx.coroutines.test.runTest + +import org.junit.Assert.assertEquals +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner +import org.robolectric.annotation.Config + +@RunWith(RobolectricTestRunner::class) +@Config(sdk = [Build.VERSION_CODES.Q]) +class ActionTextViewModelTests { + + @Test + fun setTextCounterReference_isWrittenToTheEditedAction() = runTest { + val scenario = Scenario(Identifier(databaseId = 1L), "Scenario", detectionQuality = 600) + val event = ScreenEvent(Identifier(databaseId = 2L), scenario.id, "Event", AND, priority = 0, keepDetecting = false, cooldownMs = 0) + val action = SetText(Identifier(databaseId = 3L), event.id, priority = 0, text = "Hello ", validateInput = false) + val editionRepository = createEditionRepository(scenario, screenEvents = listOf(event)) + editionRepository.startEventEdition(event) + editionRepository.startActionEdition(action) + + SetTextViewModel(editionRepository).appendCounterReferenceToTextToWrite("score") + + assertEquals("Hello ".appendCounterReference("score"), editionRepository.editionState.getEditedAction()?.text) + } + + @Test + fun notificationCounterReference_isWrittenToTheEditedAction() = runTest { + val scenario = Scenario(Identifier(databaseId = 1L), "Scenario", detectionQuality = 600) + val event = ScreenEvent(Identifier(databaseId = 2L), scenario.id, "Event", AND, priority = 0, keepDetecting = false, cooldownMs = 0) + val action = Notification(Identifier(databaseId = 3L), event.id, priority = 0, messageText = "Done: ", channelImportance = 3) + val editionRepository = createEditionRepository(scenario, screenEvents = listOf(event)) + editionRepository.startEventEdition(event) + editionRepository.startActionEdition(action) + + NotificationViewModel(editionRepository).setNotificationMessage("Done: ".appendCounterReference("score")) + + assertEquals("Done: ".appendCounterReference("score"), editionRepository.editionState.getEditedAction()?.messageText) + } +} diff --git a/feature/smart-config/src/test/java/com/buzbuz/smartautoclicker/feature/smart/config/ui/condition/screen/number/NumberConditionViewModelTests.kt b/feature/smart-config/src/test/java/com/buzbuz/smartautoclicker/feature/smart/config/ui/condition/screen/number/NumberConditionViewModelTests.kt new file mode 100644 index 000000000..bae300c8f --- /dev/null +++ b/feature/smart-config/src/test/java/com/buzbuz/smartautoclicker/feature/smart/config/ui/condition/screen/number/NumberConditionViewModelTests.kt @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2026 Kevin Buzeau + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + */ +package com.buzbuz.smartautoclicker.feature.smart.config.ui.condition.screen.number + +import android.content.Context +import android.graphics.Rect +import android.os.Build + +import com.buzbuz.smartautoclicker.core.base.identifier.Identifier +import com.buzbuz.smartautoclicker.core.common.tutorial.domain.MonitoredViewsManager +import com.buzbuz.smartautoclicker.core.domain.model.AND +import com.buzbuz.smartautoclicker.core.domain.model.condition.NumberFormatType +import com.buzbuz.smartautoclicker.core.domain.model.condition.ScreenCondition +import com.buzbuz.smartautoclicker.core.domain.model.counter.ComparisonOperation +import com.buzbuz.smartautoclicker.core.domain.model.counter.CounterOperationValue +import com.buzbuz.smartautoclicker.core.domain.model.event.ScreenEvent +import com.buzbuz.smartautoclicker.core.domain.model.scenario.Scenario +import com.buzbuz.smartautoclicker.feature.smart.config.ui.common.model.condition.UiNumberFormatDropdownItem +import com.buzbuz.smartautoclicker.feature.smart.config.ui.common.model.counter.UiCounterOperatorDropdownItem +import com.buzbuz.smartautoclicker.feature.smart.config.ui.common.model.counter.UiOperandType +import com.buzbuz.smartautoclicker.feature.smart.config.ui.createEditionRepository + +import io.mockk.mockk + +import kotlinx.coroutines.test.runTest + +import org.junit.Assert.assertEquals +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner +import org.robolectric.annotation.Config + +@RunWith(RobolectricTestRunner::class) +@Config(sdk = [Build.VERSION_CODES.Q]) +class NumberConditionViewModelTests { + + @Test + fun changesKeepUnrelatedNumberConditionFieldsAndUpdateSelectedValues() = runTest { + val scenario = Scenario(Identifier(databaseId = 1L), "Scenario", detectionQuality = 600) + val event = ScreenEvent( + id = Identifier(databaseId = 2L), scenarioId = scenario.id, name = "Event", conditionOperator = AND, + priority = 0, keepDetecting = false, cooldownMs = 0, + ) + val condition = ScreenCondition.Number( + id = Identifier(databaseId = 3L), eventId = event.id, name = "Amount", threshold = 80, priority = 0, + detectionArea = Rect(1, 2, 30, 40), comparisonOperation = ComparisonOperation.EQUALS, + counterValue = CounterOperationValue.Number(12.0), numberFormatType = NumberFormatType.AUTO, + ) + val editionRepository = createEditionRepository(scenario, screenEvents = listOf(event)) + editionRepository.startEventEdition(event) + editionRepository.startConditionEdition(condition) + val viewModel = NumberConditionViewModel(mockk(), editionRepository, mockk()) + + viewModel.setNumberFormat(UiNumberFormatDropdownItem.CommaDecimal) + viewModel.setComparisonOperator(UiCounterOperatorDropdownItem.Comparison.GreaterOrEqualsItem) + viewModel.setThreshold(65) + viewModel.setDetectionArea(Rect(5, 6, 70, 80)) + viewModel.setOperandType(UiOperandType.COUNTER) + + assertEquals( + condition.copy( + threshold = 65, + detectionArea = Rect(5, 6, 70, 80), + comparisonOperation = ComparisonOperation.GREATER_OR_EQUALS, + counterValue = CounterOperationValue.Counter(""), + numberFormatType = NumberFormatType.COMMA_DECIMAL, + ), + editionRepository.editionState.getEditedCondition(), + ) + + viewModel.setOperandType(UiOperandType.STATIC) + + assertEquals( + CounterOperationValue.Number(0.0), + editionRepository.editionState.getEditedCondition()?.counterValue, + ) + } +} diff --git a/feature/smart-config/src/test/java/com/buzbuz/smartautoclicker/feature/smart/config/ui/event/EventDialogViewModelTests.kt b/feature/smart-config/src/test/java/com/buzbuz/smartautoclicker/feature/smart/config/ui/event/EventDialogViewModelTests.kt new file mode 100644 index 000000000..cdd70b7e8 --- /dev/null +++ b/feature/smart-config/src/test/java/com/buzbuz/smartautoclicker/feature/smart/config/ui/event/EventDialogViewModelTests.kt @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2026 Kevin Buzeau + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + */ +package com.buzbuz.smartautoclicker.feature.smart.config.ui.event + +import android.content.Context +import android.os.Build + +import com.buzbuz.smartautoclicker.core.bitmaps.BitmapRepository +import com.buzbuz.smartautoclicker.core.base.identifier.Identifier +import com.buzbuz.smartautoclicker.core.common.tutorial.domain.MonitoredViewsManager +import com.buzbuz.smartautoclicker.core.common.tutorial.domain.TutorialRepository +import com.buzbuz.smartautoclicker.core.domain.IRepository +import com.buzbuz.smartautoclicker.core.domain.model.AND +import com.buzbuz.smartautoclicker.core.domain.model.event.TriggerEvent +import com.buzbuz.smartautoclicker.core.domain.model.scenario.Scenario +import com.buzbuz.smartautoclicker.core.settings.domain.SettingsRepository +import com.buzbuz.smartautoclicker.feature.smart.config.domain.EditionRepository +import com.buzbuz.smartautoclicker.feature.smart.config.ui.createEditionRepository + +import io.mockk.coEvery +import io.mockk.mockk + +import kotlinx.coroutines.test.runTest + +import org.junit.Assert.assertFalse +import org.junit.Assert.assertEquals +import org.junit.Assert.assertNull +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner +import org.robolectric.annotation.Config + +/** Regression tests for opening the shared event dialog while a trigger event is edited. */ +@RunWith(RobolectricTestRunner::class) +@Config(sdk = [Build.VERSION_CODES.Q]) +class EventDialogViewModelTests { + + @Test + fun triggerEvent_doesNotPretendToBeScreenEvent_orExposeTryInfo() = runTest { + val scenario = Scenario( + id = Identifier(databaseId = 1L), + name = "Scenario", + detectionQuality = 600, + ) + val triggerEvent = TriggerEvent( + id = Identifier(databaseId = 2L), + scenarioId = scenario.id, + name = "Trigger event", + conditionOperator = AND, + ) + val repository = mockk { + coEvery { getScenario(1L) } returns scenario + coEvery { getScreenEvents(1L) } returns emptyList() + coEvery { getTriggerEvents(1L) } returns emptyList() + coEvery { getCounters(1L) } returns emptyList() + } + val editionRepository = EditionRepository(repository, mockk(relaxed = true)) + editionRepository.startEdition(1L) + editionRepository.startEventEdition(triggerEvent) + + val viewModel = EventDialogViewModel( + context = mockk(), + bitmapRepository = mockk(), + editionRepository = editionRepository, + monitoredViewsManager = mockk(), + settingsRepository = mockk(), + tutorialRepository = mockk(), + ) + + assertFalse(viewModel.isConfiguringScreenEvent()) + assertNull(viewModel.getTryInfo()) + } + + @Test + fun triggerEvent_ignoresScreenOnlyControls() = runTest { + val scenario = Scenario(Identifier(databaseId = 1L), "Scenario", detectionQuality = 600) + val triggerEvent = TriggerEvent(Identifier(databaseId = 2L), scenario.id, "Trigger event", AND) + val editionRepository = createEditionRepository(scenario) + editionRepository.startEventEdition(triggerEvent) + val viewModel = EventDialogViewModel( + context = mockk(), + bitmapRepository = mockk(), + editionRepository = editionRepository, + monitoredViewsManager = mockk(), + settingsRepository = mockk(), + tutorialRepository = mockk(), + ) + + viewModel.toggleKeepDetectingState() + viewModel.toggleCooldownState() + viewModel.setCooldownValue(500) + + assertEquals(triggerEvent, editionRepository.editionState.getEditedEvent()) + } +} diff --git a/feature/smart-config/src/test/java/com/buzbuz/smartautoclicker/feature/smart/config/ui/scenario/ScenarioDialogViewModelTests.kt b/feature/smart-config/src/test/java/com/buzbuz/smartautoclicker/feature/smart/config/ui/scenario/ScenarioDialogViewModelTests.kt new file mode 100644 index 000000000..2b58b224a --- /dev/null +++ b/feature/smart-config/src/test/java/com/buzbuz/smartautoclicker/feature/smart/config/ui/scenario/ScenarioDialogViewModelTests.kt @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2026 Kevin Buzeau + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + */ +package com.buzbuz.smartautoclicker.feature.smart.config.ui.scenario + +import android.os.Build + +import com.buzbuz.smartautoclicker.core.base.identifier.Identifier +import com.buzbuz.smartautoclicker.core.common.tutorial.domain.MonitoredViewsManager +import com.buzbuz.smartautoclicker.core.domain.model.counter.Counter +import com.buzbuz.smartautoclicker.core.domain.model.scenario.Scenario +import com.buzbuz.smartautoclicker.feature.smart.config.ui.createEditionRepository + +import io.mockk.mockk + +import kotlinx.coroutines.test.runTest + +import org.junit.Assert.assertTrue +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner +import org.robolectric.annotation.Config +import org.robolectric.Shadows.shadowOf + +@RunWith(RobolectricTestRunner::class) +@Config(sdk = [Build.VERSION_CODES.Q]) +class ScenarioDialogViewModelTests { + + @Test + fun counterOnlyChange_marksScenarioAsHavingUnsavedModifications() = runTest { + val scenario = Scenario(Identifier(databaseId = 1L), "Scenario", detectionQuality = 600) + val editionRepository = createEditionRepository(scenario) + val viewModel = ScenarioDialogViewModel(editionRepository, mockk()) + shadowOf(android.os.Looper.getMainLooper()).idle() + + editionRepository.addNewCounter(Counter("count", 2.0, scenario.id)) + shadowOf(android.os.Looper.getMainLooper()).idle() + + assertTrue(viewModel.hasUnsavedModifications()) + } +} diff --git a/feature/smart-config/src/test/java/com/buzbuz/smartautoclicker/feature/smart/config/ui/scenario/triggerevents/TriggerEventListViewModelTests.kt b/feature/smart-config/src/test/java/com/buzbuz/smartautoclicker/feature/smart/config/ui/scenario/triggerevents/TriggerEventListViewModelTests.kt new file mode 100644 index 000000000..7dae6cf8a --- /dev/null +++ b/feature/smart-config/src/test/java/com/buzbuz/smartautoclicker/feature/smart/config/ui/scenario/triggerevents/TriggerEventListViewModelTests.kt @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2026 Kevin Buzeau + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + */ +package com.buzbuz.smartautoclicker.feature.smart.config.ui.scenario.triggerevents + +import android.os.Build + +import com.buzbuz.smartautoclicker.core.base.identifier.Identifier +import com.buzbuz.smartautoclicker.core.domain.model.AND +import com.buzbuz.smartautoclicker.core.domain.model.event.TriggerEvent +import com.buzbuz.smartautoclicker.core.domain.model.scenario.Scenario +import com.buzbuz.smartautoclicker.feature.smart.config.domain.usecase.copy.availability.IsTriggerEventCopyAvailableUseCase +import com.buzbuz.smartautoclicker.feature.smart.config.ui.createEditionRepository + +import io.mockk.mockk + +import kotlinx.coroutines.test.runTest + +import org.junit.Assert.assertEquals +import org.junit.Assert.assertNull +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner +import org.robolectric.RuntimeEnvironment +import org.robolectric.annotation.Config + +@RunWith(RobolectricTestRunner::class) +@Config(sdk = [Build.VERSION_CODES.Q]) +class TriggerEventListViewModelTests { + + @Test + fun createNewEvent_belongsToTheEditedScenario() = runTest { + val scenario = Scenario(Identifier(databaseId = 1L), "Scenario", detectionQuality = 600) + val editionRepository = createEditionRepository(scenario) + val viewModel = TriggerEventListViewModel( + mockk(relaxed = true), + editionRepository, + ) + + val event = viewModel.createNewEvent(RuntimeEnvironment.getApplication()) + + assertEquals(scenario.id, event.scenarioId) + } + + @Test + fun editSaveDeleteAndDismiss_updateTheEditedTriggerEvent() = runTest { + val scenario = Scenario(Identifier(databaseId = 1L), "Scenario", detectionQuality = 600) + val triggerEvent = TriggerEvent(Identifier(databaseId = 2L), scenario.id, "Trigger event", AND) + val editionRepository = createEditionRepository(scenario, triggerEvents = listOf(triggerEvent)) + val viewModel = TriggerEventListViewModel( + mockk(relaxed = true), + editionRepository, + ) + + viewModel.startEventEdition(triggerEvent) + editionRepository.updateEditedEvent(triggerEvent.copy(name = "Renamed trigger")) + viewModel.saveEventEdition() + assertEquals("Renamed trigger", editionRepository.editionState.getAllEditedEvents().single().name) + + viewModel.startEventEdition(editionRepository.editionState.getAllEditedEvents().single() as TriggerEvent) + viewModel.dismissEditedEvent() + assertNull(editionRepository.editionState.getEditedEvent()) + + viewModel.startEventEdition(editionRepository.editionState.getAllEditedEvents().single() as TriggerEvent) + viewModel.deleteEditedEvent() + assertEquals(emptyList(), editionRepository.editionState.getAllEditedEvents()) + } +}