[FeatureFlag] add more tests
This commit is contained in:
@@ -57,6 +57,7 @@ dependencies {
|
||||
testImplementation(libs.test.truth)
|
||||
testImplementation(libs.test.turbine)
|
||||
testImplementation(projects.libraries.matrix.test)
|
||||
testImplementation(projects.libraries.featureflag.test)
|
||||
testImplementation(projects.features.rageshake.test)
|
||||
testImplementation(projects.features.rageshake.impl)
|
||||
testImplementation(projects.features.logout.impl)
|
||||
|
||||
@@ -21,24 +21,25 @@ import io.element.android.features.logout.api.LogoutPreferencePresenter
|
||||
import io.element.android.features.rageshake.api.preferences.RageshakePreferencesPresenter
|
||||
import io.element.android.libraries.architecture.Async
|
||||
import io.element.android.libraries.architecture.Presenter
|
||||
import io.element.android.libraries.core.meta.BuildMeta
|
||||
import io.element.android.libraries.core.meta.BuildType
|
||||
import javax.inject.Inject
|
||||
|
||||
class PreferencesRootPresenter @Inject constructor(
|
||||
private val logoutPresenter: LogoutPreferencePresenter,
|
||||
private val rageshakePresenter: RageshakePreferencesPresenter,
|
||||
private val buildMeta: BuildMeta,
|
||||
private val buildType: BuildType,
|
||||
) : Presenter<PreferencesRootState> {
|
||||
|
||||
@Composable
|
||||
override fun present(): PreferencesRootState {
|
||||
val logoutState = logoutPresenter.present()
|
||||
val rageshakeState = rageshakePresenter.present()
|
||||
val showDeveloperSettings = buildType != BuildType.RELEASE
|
||||
return PreferencesRootState(
|
||||
logoutState = logoutState,
|
||||
rageshakeState = rageshakeState,
|
||||
myUser = Async.Uninitialized,
|
||||
showDeveloperSettings = buildMeta.isDebug
|
||||
showDeveloperSettings = showDeveloperSettings
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Copyright (c) 2023 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
@file:OptIn(ExperimentalCoroutinesApi::class)
|
||||
|
||||
package io.element.android.features.preferences.impl.developer
|
||||
|
||||
import app.cash.molecule.RecompositionClock
|
||||
import app.cash.molecule.moleculeFlow
|
||||
import app.cash.turbine.test
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.libraries.featureflag.api.FeatureFlags
|
||||
import io.element.android.libraries.featureflag.test.FakeFeatureFlagService
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Test
|
||||
|
||||
class DeveloperSettingsPresenterTest {
|
||||
@Test
|
||||
fun `present - ensures initial state is correct`() = runTest {
|
||||
val presenter = DeveloperSettingsPresenter(
|
||||
FakeFeatureFlagService()
|
||||
)
|
||||
moleculeFlow(RecompositionClock.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
val initialState = awaitItem()
|
||||
assertThat(initialState.features).isEmpty()
|
||||
cancelAndIgnoreRemainingEvents()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - ensures feature list is loaded`() = runTest {
|
||||
val presenter = DeveloperSettingsPresenter(
|
||||
FakeFeatureFlagService()
|
||||
)
|
||||
moleculeFlow(RecompositionClock.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
skipItems(1)
|
||||
val state = awaitItem()
|
||||
assertThat(state.features).hasSize(FeatureFlags.values().size)
|
||||
cancelAndIgnoreRemainingEvents()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - ensures state is updated when enabled feature event is triggered`() = runTest {
|
||||
val presenter = DeveloperSettingsPresenter(
|
||||
FakeFeatureFlagService()
|
||||
)
|
||||
moleculeFlow(RecompositionClock.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
skipItems(1)
|
||||
val stateBeforeEvent = awaitItem()
|
||||
val featureBeforeEvent = stateBeforeEvent.features.first()
|
||||
stateBeforeEvent.eventSink(DeveloperSettingsEvents.UpdateEnabledFeature(featureBeforeEvent, !featureBeforeEvent.isEnabled))
|
||||
val stateAfterEvent = awaitItem()
|
||||
val featureAfterEvent = stateAfterEvent.features.first()
|
||||
assertThat(featureBeforeEvent.key).isEqualTo(featureAfterEvent.key)
|
||||
assertThat(featureBeforeEvent.isEnabled).isNotEqualTo(featureAfterEvent.isEnabled)
|
||||
cancelAndIgnoreRemainingEvents()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -27,6 +27,7 @@ import io.element.android.features.rageshake.impl.preferences.DefaultRageshakePr
|
||||
import io.element.android.features.rageshake.test.rageshake.FakeRageShake
|
||||
import io.element.android.features.rageshake.test.rageshake.FakeRageshakeDataStore
|
||||
import io.element.android.libraries.architecture.Async
|
||||
import io.element.android.libraries.core.meta.BuildType
|
||||
import io.element.android.libraries.matrix.test.FakeMatrixClient
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.test.runTest
|
||||
@@ -38,7 +39,9 @@ class PreferencesRootPresenterTest {
|
||||
val logoutPresenter = DefaultLogoutPreferencePresenter(FakeMatrixClient())
|
||||
val rageshakePresenter = DefaultRageshakePreferencesPresenter(FakeRageShake(), FakeRageshakeDataStore())
|
||||
val presenter = PreferencesRootPresenter(
|
||||
logoutPresenter, rageshakePresenter
|
||||
logoutPresenter,
|
||||
rageshakePresenter,
|
||||
BuildType.DEBUG
|
||||
)
|
||||
moleculeFlow(RecompositionClock.Immediate) {
|
||||
presenter.present()
|
||||
@@ -50,6 +53,7 @@ class PreferencesRootPresenterTest {
|
||||
assertThat(initialState.rageshakeState.isSupported).isTrue()
|
||||
assertThat(initialState.rageshakeState.sensitivity).isEqualTo(1.0f)
|
||||
assertThat(initialState.myUser).isEqualTo(Async.Uninitialized)
|
||||
assertThat(initialState.showDeveloperSettings).isEqualTo(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
package io.element.android.libraries.featureflag.api
|
||||
|
||||
interface FeatureFlagService {
|
||||
|
||||
/**
|
||||
* @param feature the feature to check for
|
||||
*
|
||||
|
||||
@@ -20,36 +20,29 @@ import com.squareup.anvil.annotations.ContributesTo
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.multibindings.ElementsIntoSet
|
||||
import io.element.android.libraries.core.meta.BuildMeta
|
||||
import io.element.android.libraries.core.meta.BuildType
|
||||
import io.element.android.libraries.di.AppScope
|
||||
import io.element.android.libraries.featureflag.impl.BuildtimeFeatureFlagProvider
|
||||
import io.element.android.libraries.featureflag.impl.FeatureFlagProvider
|
||||
import io.element.android.libraries.featureflag.impl.PreferencesFeatureFlagProvider
|
||||
import io.element.android.libraries.featureflag.impl.RuntimeFeatureFlagProvider
|
||||
|
||||
@Module
|
||||
@ContributesTo(AppScope::class)
|
||||
object FeatureFlagModule {
|
||||
|
||||
@Provides
|
||||
fun providesRuntimeFeatureFlagProvider(preferencesFeatureFlagProvider: PreferencesFeatureFlagProvider): RuntimeFeatureFlagProvider {
|
||||
return preferencesFeatureFlagProvider
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
@Provides
|
||||
@ElementsIntoSet
|
||||
fun providesFeatureFlagProvider(
|
||||
buildMeta: BuildMeta,
|
||||
buildType: BuildType,
|
||||
runtimeFeatureFlagProvider: PreferencesFeatureFlagProvider,
|
||||
buildtimeFeatureFlagProvider: BuildtimeFeatureFlagProvider,
|
||||
runtimeFeatureFlagProvider: RuntimeFeatureFlagProvider,
|
||||
): Set<FeatureFlagProvider> {
|
||||
val providers = HashSet<FeatureFlagProvider>()
|
||||
//TODO change this condition?
|
||||
if (buildMeta.isDebug) {
|
||||
providers.add(runtimeFeatureFlagProvider)
|
||||
} else {
|
||||
if (buildType == BuildType.RELEASE) {
|
||||
providers.add(buildtimeFeatureFlagProvider)
|
||||
} else {
|
||||
providers.add(runtimeFeatureFlagProvider)
|
||||
}
|
||||
return providers
|
||||
}
|
||||
|
||||
27
libraries/featureflag/test/build.gradle.kts
Normal file
27
libraries/featureflag/test/build.gradle.kts
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (c) 2023 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
plugins {
|
||||
id("io.element.android-library")
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "io.element.android.libraries.featureflag.test"
|
||||
|
||||
dependencies {
|
||||
api(projects.libraries.featureflag.api)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright (c) 2023 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.featureflag.test
|
||||
|
||||
import io.element.android.libraries.featureflag.api.Feature
|
||||
import io.element.android.libraries.featureflag.api.FeatureFlagService
|
||||
|
||||
class FakeFeatureFlagService(
|
||||
initialState: Map<String, Boolean> = emptyMap()
|
||||
) : FeatureFlagService {
|
||||
|
||||
private val enabledFeatures = HashMap(initialState)
|
||||
|
||||
override suspend fun setFeatureEnabled(feature: Feature, enabled: Boolean): Boolean {
|
||||
enabledFeatures[feature.key] = enabled
|
||||
return true
|
||||
}
|
||||
|
||||
override suspend fun isFeatureEnabled(feature: Feature): Boolean {
|
||||
return enabledFeatures[feature.key] ?: feature.defaultValue
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
import java.net.URI
|
||||
|
||||
include(":libraries:featureflag:test")
|
||||
|
||||
include(":libraries:featureflag:ui")
|
||||
|
||||
/*
|
||||
|
||||
Reference in New Issue
Block a user