feat(ui): add clean SettingsViewModel and comprehensive ViewModel JVM unit tests
This commit is contained in:
parent
dfdd9e761f
commit
d0cb89cf6e
2 changed files with 134 additions and 0 deletions
|
|
@ -0,0 +1,67 @@
|
||||||
|
package dev.kaizen.app.settings
|
||||||
|
|
||||||
|
import androidx.compose.runtime.State
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
|
|
||||||
|
// Available Kaizen UI themes
|
||||||
|
enum class KaizenThemeId { Amber, Blue, Aurora, Mono }
|
||||||
|
|
||||||
|
// Available Light/Dark/System appearance modes
|
||||||
|
enum class AppAppearance { Light, Dark, System }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clean MVVM ViewModel for the Settings module.
|
||||||
|
*
|
||||||
|
* Completely decoupled from Android UI classes, making it 100% unit-testable
|
||||||
|
* on the JVM. Manages the state of the Oklab Theme Picker, selected model,
|
||||||
|
* and user profile options.
|
||||||
|
*/
|
||||||
|
class SettingsViewModel : ViewModel() {
|
||||||
|
|
||||||
|
// 1. Reactive State Flow for Theme
|
||||||
|
private val _selectedTheme = MutableStateFlow(KaizenThemeId.Amber)
|
||||||
|
val selectedTheme: StateFlow<KaizenThemeId> = _selectedTheme.asStateFlow()
|
||||||
|
|
||||||
|
// 2. Reactive State Flow for Appearance
|
||||||
|
private val _appearanceMode = MutableStateFlow(AppAppearance.Dark)
|
||||||
|
val appearanceMode: StateFlow<AppAppearance> = _appearanceMode.asStateFlow()
|
||||||
|
|
||||||
|
// 3. User profile details
|
||||||
|
private val _userName = MutableStateFlow("Bruno")
|
||||||
|
val userName: StateFlow<String> = _userName.asStateFlow()
|
||||||
|
|
||||||
|
private val _userEmail = MutableStateFlow("admin@kryptomrx.de")
|
||||||
|
val userEmail: StateFlow<String> = _userEmail.asStateFlow()
|
||||||
|
|
||||||
|
// 4. Default Chat Model
|
||||||
|
private val _defaultModel = MutableStateFlow("gemini-2.5-flash")
|
||||||
|
val defaultModel: StateFlow<String> = _defaultModel.asStateFlow()
|
||||||
|
|
||||||
|
/** Selects a new Kaizen UI theme and triggers an Oklab transition stops recalculation */
|
||||||
|
fun selectTheme(themeId: KaizenThemeId) {
|
||||||
|
_selectedTheme.value = themeId
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Changes the appearance mode (Light, Dark, or System Sync) */
|
||||||
|
fun selectAppearance(mode: AppAppearance) {
|
||||||
|
_appearanceMode.value = mode
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Updates the user's name in profile settings */
|
||||||
|
fun updateUserName(name: String) {
|
||||||
|
if (name.isNotBlank()) {
|
||||||
|
_userName.value = name.trim()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Updates the standard chat model */
|
||||||
|
fun updateDefaultModel(modelId: String) {
|
||||||
|
if (modelId.isNotBlank()) {
|
||||||
|
_defaultModel.value = modelId.trim()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
67
app/src/test/java/dev/kaizen/app/SettingsViewModelTest.kt
Normal file
67
app/src/test/java/dev/kaizen/app/SettingsViewModelTest.kt
Normal file
|
|
@ -0,0 +1,67 @@
|
||||||
|
package dev.kaizen.app
|
||||||
|
|
||||||
|
import dev.kaizen.app.settings.AppAppearance
|
||||||
|
import dev.kaizen.app.settings.KaizenThemeId
|
||||||
|
import dev.kaizen.app.settings.SettingsViewModel
|
||||||
|
import org.junit.Assert.assertEquals
|
||||||
|
import org.junit.Test
|
||||||
|
|
||||||
|
/**
|
||||||
|
* High-fidelity JVM Unit Test for the SettingsViewModel.
|
||||||
|
*
|
||||||
|
* Verifies that state transitions, user inputs, and theme selections are
|
||||||
|
* correctly and reactively managed inside the view model under clean MVVM.
|
||||||
|
*/
|
||||||
|
class SettingsViewModelTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testInitialState() {
|
||||||
|
val viewModel = SettingsViewModel()
|
||||||
|
|
||||||
|
// Assert correct default states
|
||||||
|
assertEquals(KaizenThemeId.Amber, viewModel.selectedTheme.value)
|
||||||
|
assertEquals(AppAppearance.Dark, viewModel.appearanceMode.value)
|
||||||
|
assertEquals("Bruno", viewModel.userName.value)
|
||||||
|
assertEquals("gemini-2.5-flash", viewModel.defaultModel.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testSelectTheme() {
|
||||||
|
val viewModel = SettingsViewModel()
|
||||||
|
|
||||||
|
// Transition theme to Aurora
|
||||||
|
viewModel.selectTheme(KaizenThemeId.Aurora)
|
||||||
|
|
||||||
|
assertEquals(KaizenThemeId.Aurora, viewModel.selectedTheme.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testSelectAppearance() {
|
||||||
|
val viewModel = SettingsViewModel()
|
||||||
|
|
||||||
|
// Switch appearance to Light Mode
|
||||||
|
viewModel.selectAppearance(AppAppearance.Light)
|
||||||
|
|
||||||
|
assertEquals(AppAppearance.Light, viewModel.appearanceMode.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testUpdateUserName() {
|
||||||
|
val viewModel = SettingsViewModel()
|
||||||
|
|
||||||
|
// Update user's name
|
||||||
|
viewModel.updateUserName(" Giuseppe ") // Should be trimmed
|
||||||
|
|
||||||
|
assertEquals("Giuseppe", viewModel.userName.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testUpdateUserNameBlankIgnored() {
|
||||||
|
val viewModel = SettingsViewModel()
|
||||||
|
|
||||||
|
// Attempting to set an empty/blank name should be ignored
|
||||||
|
viewModel.updateUserName(" ")
|
||||||
|
|
||||||
|
assertEquals("Bruno", viewModel.userName.value) // Remains unchanged
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue