Add empty state for debug menu without feature flags (#5918)

This commit is contained in:
David Perez
2025-09-22 15:30:25 -05:00
committed by GitHub
parent ad46d8d7c0
commit fa5053b5cc
8 changed files with 79 additions and 71 deletions

View File

@@ -23,6 +23,7 @@ import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.bitwarden.authenticator.ui.platform.components.appbar.AuthenticatorTopAppBar
import com.bitwarden.authenticator.ui.platform.components.button.AuthenticatorFilledButton
import com.bitwarden.authenticator.ui.platform.components.content.AuthenticatorErrorContent
import com.bitwarden.authenticator.ui.platform.components.header.BitwardenListHeaderText
import com.bitwarden.authenticator.ui.platform.components.scaffold.BitwardenScaffold
import com.bitwarden.authenticator.ui.platform.feature.debugmenu.components.ListItemContent
@@ -74,12 +75,14 @@ fun DebugMenuScreen(
)
},
) { innerPadding ->
Column(
modifier = Modifier
.verticalScroll(rememberScrollState())
.padding(innerPadding),
) {
Spacer(modifier = Modifier.height(16.dp))
if (state.featureFlags.isEmpty()) {
AuthenticatorErrorContent(
message = stringResource(id = BitwardenString.empty_item_list),
modifier = Modifier
.padding(paddingValues = innerPadding)
.fillMaxSize(),
)
} else {
FeatureFlagContent(
featureFlagMap = state.featureFlags,
onValueChange = remember(viewModel) {
@@ -88,10 +91,11 @@ fun DebugMenuScreen(
}
},
onResetValues = remember(viewModel) {
{
viewModel.trySendAction(DebugMenuAction.ResetFeatureFlagValues)
}
{ viewModel.trySendAction(DebugMenuAction.ResetFeatureFlagValues) }
},
modifier = Modifier
.verticalScroll(rememberScrollState())
.padding(paddingValues = innerPadding),
)
}
}

View File

@@ -6,6 +6,9 @@ import com.bitwarden.authenticator.data.platform.repository.DebugMenuRepository
import com.bitwarden.core.data.manager.model.FlagKey
import com.bitwarden.ui.platform.base.BaseViewModel
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.collections.immutable.ImmutableMap
import kotlinx.collections.immutable.persistentMapOf
import kotlinx.collections.immutable.toImmutableMap
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.launchIn
@@ -23,7 +26,7 @@ class DebugMenuViewModel @Inject constructor(
featureFlagManager: FeatureFlagManager,
private val debugMenuRepository: DebugMenuRepository,
) : BaseViewModel<DebugMenuState, DebugMenuEvent, DebugMenuAction>(
initialState = DebugMenuState(featureFlags = emptyMap()),
initialState = DebugMenuState(featureFlags = persistentMapOf()),
) {
private var featureFlagResetJob: Job? = null
@@ -60,7 +63,7 @@ class DebugMenuViewModel @Inject constructor(
private fun handleUpdateFeatureFlagMap(action: DebugMenuAction.Internal.UpdateFeatureFlagMap) {
mutableStateFlow.update {
it.copy(featureFlags = action.newMap)
it.copy(featureFlags = action.newMap.toImmutableMap())
}
}
@@ -73,7 +76,7 @@ class DebugMenuViewModel @Inject constructor(
* State for the [DebugMenuViewModel]
*/
data class DebugMenuState(
val featureFlags: Map<FlagKey<Any>, Any>,
val featureFlags: ImmutableMap<FlagKey<Any>, Any>,
)
/**

View File

@@ -17,11 +17,11 @@ fun <T : Any> FlagKey<T>.ListItemContent(
onValueChange: (key: FlagKey<T>, value: T) -> Unit,
modifier: Modifier = Modifier,
) = when (val flagKey = this) {
FlagKey.DummyBoolean,
is FlagKey.DummyInt,
FlagKey.DummyString,
-> Unit
FlagKey.DummyBoolean,
FlagKey.BitwardenAuthenticationEnabled,
FlagKey.CipherKeyEncryption,
FlagKey.CredentialExchangeProtocolExport,

View File

@@ -10,7 +10,9 @@ import com.bitwarden.core.data.repository.util.bufferedMutableSharedFlow
import io.mockk.every
import io.mockk.mockk
import io.mockk.verify
import kotlinx.collections.immutable.persistentMapOf
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.update
import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Test
@@ -18,7 +20,7 @@ import org.junit.Test
class DebugMenuScreenTest : AuthenticatorComposeTest() {
private var onNavigateBackCalled = false
private val mutableEventFlow = bufferedMutableSharedFlow<DebugMenuEvent>()
private val mutableStateFlow = MutableStateFlow(DebugMenuState(featureFlags = emptyMap()))
private val mutableStateFlow = MutableStateFlow(DEFAULT_STATE)
private val viewModel = mockk<DebugMenuViewModel>(relaxed = true) {
every { stateFlow } returns mutableStateFlow
every { eventFlow } returns mutableEventFlow
@@ -51,43 +53,31 @@ class DebugMenuScreenTest : AuthenticatorComposeTest() {
@Test
fun `feature flag content should not display if the state is empty`() {
mutableStateFlow.update {
DebugMenuState(featureFlags = persistentMapOf())
}
composeTestRule
.onNodeWithText("Bitwarden authentication enabled", ignoreCase = true)
.onNodeWithText(text = "dummy-boolean")
.assertDoesNotExist()
}
@Test
fun `feature flag content should display if the state is not empty`() {
mutableStateFlow.tryEmit(
DebugMenuState(
featureFlags = mapOf(
FlagKey.BitwardenAuthenticationEnabled to true,
),
),
)
composeTestRule
.onNodeWithText("Bitwarden authentication enabled", ignoreCase = true)
.onNodeWithText(text = "dummy-boolean")
.assertExists()
}
@Test
fun `boolean feature flag content should send action when clicked`() {
mutableStateFlow.tryEmit(
DebugMenuState(
featureFlags = mapOf(
FlagKey.BitwardenAuthenticationEnabled to true,
),
),
)
composeTestRule
.onNodeWithText("Bitwarden authentication enabled", ignoreCase = true)
.onNodeWithText(text = "dummy-boolean")
.performClick()
verify {
viewModel.trySendAction(
DebugMenuAction.UpdateFeatureFlag(
FlagKey.BitwardenAuthenticationEnabled,
FlagKey.DummyBoolean,
false,
),
)
@@ -104,3 +94,9 @@ class DebugMenuScreenTest : AuthenticatorComposeTest() {
verify { viewModel.trySendAction(DebugMenuAction.ResetFeatureFlagValues) }
}
}
private val DEFAULT_STATE: DebugMenuState = DebugMenuState(
featureFlags = persistentMapOf(
FlagKey.DummyBoolean to true,
),
)

View File

@@ -12,6 +12,8 @@ import io.mockk.just
import io.mockk.mockk
import io.mockk.runs
import io.mockk.verify
import kotlinx.collections.immutable.ImmutableMap
import kotlinx.collections.immutable.persistentMapOf
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.test.runTest
import org.junit.jupiter.api.Assertions.assertEquals
@@ -77,11 +79,11 @@ class DebugMenuViewModelTest : BaseViewModelTest() {
)
}
private val DEFAULT_MAP_VALUE: Map<FlagKey<Any>, Any> = mapOf(
private val DEFAULT_MAP_VALUE: ImmutableMap<FlagKey<Any>, Any> = persistentMapOf(
FlagKey.BitwardenAuthenticationEnabled to true,
)
private val UPDATED_MAP_VALUE: Map<FlagKey<Any>, Any> = mapOf(
private val UPDATED_MAP_VALUE: ImmutableMap<FlagKey<Any>, Any> = persistentMapOf(
FlagKey.BitwardenAuthenticationEnabled to false,
)