Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package com.worldline.devview.featureflip

import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.ui.test.assertCountEquals
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.onAllNodesWithTag
import androidx.compose.ui.test.onNodeWithContentDescription
import androidx.compose.ui.test.onNodeWithTag
Expand Down Expand Up @@ -106,6 +107,52 @@ class FeatureFlipScreenTest {
waitUntilTagCount(tag = "feature_item_new_checkout", expectedCount = 0)
}

@Test
fun featureFlipScreen_type_badges_are_displayed() = runComposeUiTest {
val handler = FeatureHandler(
dataStore = FakePreferencesDataStore(),
initialFeatures = listOf(
Feature.LocalFeature("dark_mode", null, false),
Feature.RemoteFeature("new_checkout", null, true, FeatureState.REMOTE)
)
)

setContent {
CompositionLocalProvider(LocalFeatureHandler provides handler) {
FeatureFlipScreen()
}
}

waitUntilTagCount(tag = "feature_item_dark_mode", expectedCount = 1)
waitUntilTagCount(tag = "feature_item_new_checkout", expectedCount = 1)

onNodeWithTag(testTag = "feature_type_dark_mode").assertIsDisplayed()
onNodeWithTag(testTag = "feature_type_new_checkout").assertIsDisplayed()
}

@Test
fun featureFlipScreen_selecting_local_and_remote_chips_shows_all_features() = runComposeUiTest {
val handler = FeatureHandler(
dataStore = FakePreferencesDataStore(),
initialFeatures = listOf(
Feature.LocalFeature("dark_mode", null, false),
Feature.RemoteFeature("new_checkout", null, true, FeatureState.REMOTE)
)
)

setContent {
CompositionLocalProvider(LocalFeatureHandler provides handler) {
FeatureFlipScreen()
}
}

onNodeWithTag(testTag = "feature_filter_chip_LOCAL").performClick()
onNodeWithTag(testTag = "feature_filter_chip_REMOTE").performClick()

waitUntilTagCount(tag = "feature_item_dark_mode", expectedCount = 1)
waitUntilTagCount(tag = "feature_item_new_checkout", expectedCount = 1)
}

@Test
fun featureFlipScreen_clear_filter_icon_visibility_and_behavior() = runComposeUiTest {
val handler = FeatureHandler(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,37 +137,34 @@ public fun FeatureFlipScreen(modifier: Modifier = Modifier, bottomPadding: Dp =

val filteredFeatures by remember(key1 = filterQuery, key2 = selectedFilters, key3 = features) {
derivedStateOf {
when {
filterQuery.isBlank() && selectedFilters.values.all { !it } -> features
val activeTypeFilters = selectedFilters
.filterKeys { it == FeatureFilter.LOCAL || it == FeatureFilter.REMOTE }
.filterValues { it }
.keys
val activeStateFilters = selectedFilters
.filterKeys { it == FeatureFilter.ON || it == FeatureFilter.OFF }
.filterValues { it }
.keys

selectedFilters.values.all { !it } -> features.filter { feature ->
features.filter { feature ->
val matchesQuery = filterQuery.isBlank() ||
feature.name.contains(other = filterQuery, ignoreCase = true)
}

filterQuery.isBlank() -> features.filter { feature ->
selectedFilters.filter { it.value }.all { (state, selected) ->
when (state) {
FeatureFilter.LOCAL -> feature is Feature.LocalFeature
FeatureFilter.REMOTE -> feature is Feature.RemoteFeature
FeatureFilter.ON -> feature.isEnabled
FeatureFilter.OFF -> !feature.isEnabled
} && selected
val matchesType = activeTypeFilters.isEmpty() || activeTypeFilters.any { filter ->
when (filter) {
FeatureFilter.LOCAL -> feature is Feature.LocalFeature
FeatureFilter.REMOTE -> feature is Feature.RemoteFeature
else -> false
}
}

else -> features.filter { feature ->
feature.name.contains(
other = filterQuery,
ignoreCase = true
) && selectedFilters.filter { it.value }.all { (state, selected) ->
when (state) {
FeatureFilter.LOCAL -> feature is Feature.LocalFeature
FeatureFilter.REMOTE -> feature is Feature.RemoteFeature
val matchesState =
activeStateFilters.isEmpty() || activeStateFilters.any { filter ->
when (filter) {
FeatureFilter.ON -> feature.isEnabled
FeatureFilter.OFF -> !feature.isEnabled
} && selected
else -> false
}
}
}
matchesQuery && matchesType && matchesState
}
}
}
Expand Down Expand Up @@ -357,11 +354,10 @@ private enum class FeatureFilter {
* @return List of applicable filter options
*/
fun availableEntries(features: List<Feature>): List<FeatureFilter> =
if (features.filterIsInstance<Feature.RemoteFeature>().size == features.size) {
listOf(
ON,
OFF
)
if (features.all { it is Feature.RemoteFeature } ||
features.all { it is Feature.LocalFeature }
) {
listOf(ON, OFF)
} else {
entries
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,15 @@ internal fun FeatureItem(
style = MaterialTheme.typography.bodySmall
)
}
Text(
modifier = Modifier.testTag(tag = "feature_type_${feature.name}"),
text = when (feature) {
is LocalFeature -> "Local"
is RemoteFeature -> "Remote"
},
style = MaterialTheme.typography.labelSmall,
color = MaterialTheme.colorScheme.outline
)
}

when (feature) {
Expand Down