mirror of
https://github.com/oxen-io/session-android.git
synced 2024-11-27 20:15:21 +00:00
Fix test
This commit is contained in:
parent
58c4467749
commit
ef24fb0fd1
@ -153,7 +153,7 @@ fun DisappearingMessages(
|
|||||||
.fadingEdges(scrollState),
|
.fadingEdges(scrollState),
|
||||||
verticalArrangement = Arrangement.spacedBy(16.dp)
|
verticalArrangement = Arrangement.spacedBy(16.dp)
|
||||||
) {
|
) {
|
||||||
state.cards.filter { it.options.isNotEmpty() }.forEach {
|
state.cards.forEach {
|
||||||
OptionsCard(it)
|
OptionsCard(it)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,10 @@
|
|||||||
package org.thoughtcrime.securesms.conversation.expiration
|
package org.thoughtcrime.securesms.conversation.expiration
|
||||||
|
|
||||||
|
import android.app.Application
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import androidx.annotation.StringRes
|
import androidx.annotation.StringRes
|
||||||
|
import androidx.compose.runtime.collectAsState
|
||||||
|
import androidx.lifecycle.AndroidViewModel
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.ViewModelProvider
|
import androidx.lifecycle.ViewModelProvider
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
@ -10,9 +13,11 @@ import dagger.assisted.AssistedInject
|
|||||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||||
import kotlinx.coroutines.channels.Channel
|
import kotlinx.coroutines.channels.Channel
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.SharingStarted
|
||||||
import kotlinx.coroutines.flow.asStateFlow
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
import kotlinx.coroutines.flow.receiveAsFlow
|
import kotlinx.coroutines.flow.receiveAsFlow
|
||||||
|
import kotlinx.coroutines.flow.stateIn
|
||||||
import kotlinx.coroutines.flow.update
|
import kotlinx.coroutines.flow.update
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import network.loki.messenger.BuildConfig
|
import network.loki.messenger.BuildConfig
|
||||||
@ -71,14 +76,14 @@ object NoOpCallbacks: Callbacks
|
|||||||
|
|
||||||
class ExpirationSettingsViewModel(
|
class ExpirationSettingsViewModel(
|
||||||
private val threadId: Long,
|
private val threadId: Long,
|
||||||
private val context: Context,
|
private val application: Application,
|
||||||
private val textSecurePreferences: TextSecurePreferences,
|
private val textSecurePreferences: TextSecurePreferences,
|
||||||
private val messageExpirationManager: MessageExpirationManagerProtocol,
|
private val messageExpirationManager: MessageExpirationManagerProtocol,
|
||||||
private val threadDb: ThreadDatabase,
|
private val threadDb: ThreadDatabase,
|
||||||
private val groupDb: GroupDatabase,
|
private val groupDb: GroupDatabase,
|
||||||
private val storage: Storage,
|
private val storage: Storage,
|
||||||
isNewConfigEnabled: Boolean
|
isNewConfigEnabled: Boolean
|
||||||
) : ViewModel(), Callbacks {
|
) : AndroidViewModel(application), Callbacks {
|
||||||
|
|
||||||
private val _event = Channel<Event>()
|
private val _event = Channel<Event>()
|
||||||
val event = _event.receiveAsFlow()
|
val event = _event.receiveAsFlow()
|
||||||
@ -89,7 +94,9 @@ class ExpirationSettingsViewModel(
|
|||||||
))
|
))
|
||||||
val state = _state.asStateFlow()
|
val state = _state.asStateFlow()
|
||||||
|
|
||||||
val uiState = _state.map { UiState(it) }
|
val uiState = _state
|
||||||
|
.map(::UiState)
|
||||||
|
.stateIn(viewModelScope, SharingStarted.Eagerly, UiState())
|
||||||
|
|
||||||
private var expirationConfig: ExpirationConfiguration? = null
|
private var expirationConfig: ExpirationConfiguration? = null
|
||||||
|
|
||||||
@ -161,7 +168,7 @@ class ExpirationSettingsViewModel(
|
|||||||
MessageSender.send(message, address)
|
MessageSender.send(message, address)
|
||||||
}
|
}
|
||||||
|
|
||||||
ConfigurationMessageUtilities.forceSyncConfigurationNowIfNeeded(context)
|
ConfigurationMessageUtilities.forceSyncConfigurationNowIfNeeded(application)
|
||||||
|
|
||||||
_event.send(Event.SUCCESS)
|
_event.send(Event.SUCCESS)
|
||||||
}
|
}
|
||||||
@ -174,7 +181,7 @@ class ExpirationSettingsViewModel(
|
|||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
class Factory @AssistedInject constructor(
|
class Factory @AssistedInject constructor(
|
||||||
@Assisted private val threadId: Long,
|
@Assisted private val threadId: Long,
|
||||||
@ApplicationContext private val context: Context,
|
private val application: Application,
|
||||||
private val textSecurePreferences: TextSecurePreferences,
|
private val textSecurePreferences: TextSecurePreferences,
|
||||||
private val messageExpirationManager: MessageExpirationManagerProtocol,
|
private val messageExpirationManager: MessageExpirationManagerProtocol,
|
||||||
private val threadDb: ThreadDatabase,
|
private val threadDb: ThreadDatabase,
|
||||||
@ -184,7 +191,7 @@ class ExpirationSettingsViewModel(
|
|||||||
|
|
||||||
override fun <T : ViewModel> create(modelClass: Class<T>): T = ExpirationSettingsViewModel(
|
override fun <T : ViewModel> create(modelClass: Class<T>): T = ExpirationSettingsViewModel(
|
||||||
threadId,
|
threadId,
|
||||||
context,
|
application,
|
||||||
textSecurePreferences,
|
textSecurePreferences,
|
||||||
messageExpirationManager,
|
messageExpirationManager,
|
||||||
threadDb,
|
threadDb,
|
||||||
@ -201,9 +208,9 @@ data class UiState(
|
|||||||
val callbacks: Callbacks = NoOpCallbacks
|
val callbacks: Callbacks = NoOpCallbacks
|
||||||
) {
|
) {
|
||||||
constructor(state: State): this(
|
constructor(state: State): this(
|
||||||
cards = listOf(
|
cards = listOfNotNull(
|
||||||
CardModel(GetString(R.string.activity_expiration_settings_delete_type), typeOptions(state)),
|
typeOptions(state)?.let { CardModel(GetString(R.string.activity_expiration_settings_delete_type), it) },
|
||||||
CardModel(GetString(R.string.activity_expiration_settings_timer), timeOptions(state))
|
timeOptions(state)?.let { CardModel(GetString(R.string.activity_expiration_settings_timer), it) }
|
||||||
),
|
),
|
||||||
showGroupFooter = state.isGroup && state.isNewConfigEnabled,
|
showGroupFooter = state.isGroup && state.isNewConfigEnabled,
|
||||||
callbacks = state.callbacks
|
callbacks = state.callbacks
|
||||||
@ -216,39 +223,42 @@ data class CardModel(
|
|||||||
)
|
)
|
||||||
|
|
||||||
private fun typeOptions(state: State) =
|
private fun typeOptions(state: State) =
|
||||||
if (state.isNoteToSelf || (state.isGroup && state.isNewConfigEnabled)) emptyList()
|
state.takeUnless {
|
||||||
else listOfNotNull(
|
state.isNoteToSelf || state.isGroup && state.isNewConfigEnabled
|
||||||
typeOption(
|
}?.run {
|
||||||
ExpiryType.NONE,
|
listOfNotNull(
|
||||||
state,
|
typeOption(
|
||||||
R.string.expiration_off,
|
ExpiryType.NONE,
|
||||||
contentDescription = R.string.AccessibilityId_disable_disappearing_messages,
|
state,
|
||||||
enabled = state.isSelfAdmin
|
R.string.expiration_off,
|
||||||
),
|
contentDescription = R.string.AccessibilityId_disable_disappearing_messages,
|
||||||
if (!state.isNewConfigEnabled) typeOption(
|
enabled = state.isSelfAdmin
|
||||||
ExpiryType.LEGACY,
|
),
|
||||||
state,
|
if (!state.isNewConfigEnabled) typeOption(
|
||||||
R.string.expiration_type_disappear_legacy,
|
ExpiryType.LEGACY,
|
||||||
contentDescription = R.string.expiration_type_disappear_legacy_description,
|
state,
|
||||||
enabled = state.isSelfAdmin
|
R.string.expiration_type_disappear_legacy,
|
||||||
) else null,
|
contentDescription = R.string.expiration_type_disappear_legacy_description,
|
||||||
if (!state.isGroup) typeOption(
|
enabled = state.isSelfAdmin
|
||||||
ExpiryType.AFTER_READ,
|
) else null,
|
||||||
state,
|
if (!state.isGroup) typeOption(
|
||||||
R.string.expiration_type_disappear_after_read,
|
ExpiryType.AFTER_READ,
|
||||||
R.string.expiration_type_disappear_after_read_description,
|
state,
|
||||||
contentDescription = R.string.expiration_type_disappear_after_read_description,
|
R.string.expiration_type_disappear_after_read,
|
||||||
enabled = state.isNewConfigEnabled && state.isSelfAdmin
|
R.string.expiration_type_disappear_after_read_description,
|
||||||
) else null,
|
contentDescription = R.string.expiration_type_disappear_after_read_description,
|
||||||
typeOption(
|
enabled = state.isNewConfigEnabled && state.isSelfAdmin
|
||||||
ExpiryType.AFTER_SEND,
|
) else null,
|
||||||
state,
|
typeOption(
|
||||||
R.string.expiration_type_disappear_after_send,
|
ExpiryType.AFTER_SEND,
|
||||||
R.string.expiration_type_disappear_after_read_description,
|
state,
|
||||||
contentDescription = R.string.expiration_type_disappear_after_send_description,
|
R.string.expiration_type_disappear_after_send,
|
||||||
enabled = state.isNewConfigEnabled && state.isSelfAdmin
|
R.string.expiration_type_disappear_after_read_description,
|
||||||
),
|
contentDescription = R.string.expiration_type_disappear_after_send_description,
|
||||||
)
|
enabled = state.isNewConfigEnabled && state.isSelfAdmin
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
private fun typeOption(
|
private fun typeOption(
|
||||||
type: ExpiryType,
|
type: ExpiryType,
|
||||||
@ -266,17 +276,17 @@ private fun typeOption(
|
|||||||
onClick = onClick
|
onClick = onClick
|
||||||
)
|
)
|
||||||
|
|
||||||
private fun timeOptions(state: State): List<OptionModel> =
|
private fun timeOptions(state: State) =
|
||||||
if (state.isNoteToSelf || (state.isGroup && state.isNewConfigEnabled)) timeOptionsOnly(state)
|
if (state.isNoteToSelf || (state.isGroup && state.isNewConfigEnabled)) timeOptionsOnly(state)
|
||||||
else when (state.expiryMode) {
|
else when (state.expiryMode) {
|
||||||
is ExpiryMode.Legacy -> afterReadTimes
|
is ExpiryMode.Legacy -> afterReadTimes
|
||||||
is ExpiryMode.AfterRead -> afterReadTimes
|
is ExpiryMode.AfterRead -> afterReadTimes
|
||||||
is ExpiryMode.AfterSend -> afterSendTimes
|
is ExpiryMode.AfterSend -> afterSendTimes
|
||||||
else -> emptyList()
|
else -> null
|
||||||
}.map { timeOption(it, state) }
|
}?.map { timeOption(it, state) }
|
||||||
|
|
||||||
private val afterSendTimes = listOf(12.hours, 1.days, 7.days, 14.days)
|
val afterSendTimes = listOf(12.hours, 1.days, 7.days, 14.days)
|
||||||
private val afterReadTimes = listOf(5.minutes, 1.hours) + afterSendTimes
|
val afterReadTimes = listOf(5.minutes, 1.hours) + afterSendTimes
|
||||||
|
|
||||||
private fun timeOptionsOnly(state: State) = listOfNotNull(
|
private fun timeOptionsOnly(state: State) = listOfNotNull(
|
||||||
typeOption(ExpiryType.NONE, state, R.string.arrays__off, enabled = state.isSelfAdmin),
|
typeOption(ExpiryType.NONE, state, R.string.arrays__off, enabled = state.isSelfAdmin),
|
||||||
@ -312,9 +322,9 @@ enum class ExpiryType(private val createMode: (Long) -> ExpiryMode) {
|
|||||||
fun mode(seconds: Long) = createMode(seconds)
|
fun mode(seconds: Long) = createMode(seconds)
|
||||||
|
|
||||||
fun defaultMode() = when(this) {
|
fun defaultMode() = when(this) {
|
||||||
AFTER_READ -> 43200L
|
AFTER_READ -> 12.hours
|
||||||
else -> 86400L
|
else -> 1.days
|
||||||
}.let { mode(it) }
|
}.inWholeSeconds.let(::mode)
|
||||||
}
|
}
|
||||||
|
|
||||||
private val ExpiryMode.type: ExpiryType get() = when(this) {
|
private val ExpiryMode.type: ExpiryType get() = when(this) {
|
||||||
|
@ -0,0 +1,25 @@
|
|||||||
|
package org.thoughtcrime.securesms
|
||||||
|
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
|
import kotlinx.coroutines.test.StandardTestDispatcher
|
||||||
|
import kotlinx.coroutines.test.TestDispatcher
|
||||||
|
import kotlinx.coroutines.test.resetMain
|
||||||
|
import kotlinx.coroutines.test.setMain
|
||||||
|
import org.junit.rules.TestWatcher
|
||||||
|
import org.junit.runner.Description
|
||||||
|
|
||||||
|
@ExperimentalCoroutinesApi
|
||||||
|
class MainCoroutineRule(private val dispatcher: TestDispatcher = StandardTestDispatcher()) :
|
||||||
|
TestWatcher() {
|
||||||
|
|
||||||
|
override fun starting(description: Description) {
|
||||||
|
super.starting(description)
|
||||||
|
Dispatchers.setMain(dispatcher)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun finished(description: Description) {
|
||||||
|
super.finished(description)
|
||||||
|
Dispatchers.resetMain()
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,99 @@
|
|||||||
|
package org.thoughtcrime.securesms.conversation.expiration
|
||||||
|
|
||||||
|
import android.app.Application
|
||||||
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
|
import kotlinx.coroutines.test.advanceUntilIdle
|
||||||
|
import kotlinx.coroutines.test.runTest
|
||||||
|
import network.loki.messenger.libsession_util.util.ExpiryMode
|
||||||
|
import org.hamcrest.CoreMatchers
|
||||||
|
import org.hamcrest.MatcherAssert
|
||||||
|
import org.junit.Rule
|
||||||
|
import org.junit.Test
|
||||||
|
import org.mockito.Mockito
|
||||||
|
import org.mockito.Mockito.mock
|
||||||
|
import org.mockito.kotlin.whenever
|
||||||
|
import org.session.libsession.messaging.messages.ExpirationConfiguration
|
||||||
|
import org.session.libsession.utilities.Address
|
||||||
|
import org.session.libsession.utilities.GroupRecord
|
||||||
|
import org.session.libsession.utilities.GroupUtil.CLOSED_GROUP_PREFIX
|
||||||
|
import org.session.libsession.utilities.SSKEnvironment
|
||||||
|
import org.session.libsession.utilities.TextSecurePreferences
|
||||||
|
import org.session.libsession.utilities.recipients.Recipient
|
||||||
|
import org.session.libsignal.utilities.guava.Optional
|
||||||
|
import org.thoughtcrime.securesms.MainCoroutineRule
|
||||||
|
import org.thoughtcrime.securesms.database.GroupDatabase
|
||||||
|
import org.thoughtcrime.securesms.database.Storage
|
||||||
|
import org.thoughtcrime.securesms.database.ThreadDatabase
|
||||||
|
import kotlin.time.Duration.Companion.hours
|
||||||
|
|
||||||
|
@OptIn(ExperimentalCoroutinesApi::class)
|
||||||
|
class ExpirationSettingsViewModelTest {
|
||||||
|
|
||||||
|
@ExperimentalCoroutinesApi
|
||||||
|
@get:Rule
|
||||||
|
var mainCoroutineRule = MainCoroutineRule()
|
||||||
|
|
||||||
|
val application: Application = mock(Application::class.java)
|
||||||
|
val textSecurePreferences: TextSecurePreferences = mock(TextSecurePreferences::class.java)
|
||||||
|
val messageExpirationManager: SSKEnvironment.MessageExpirationManagerProtocol = mock(SSKEnvironment.MessageExpirationManagerProtocol::class.java)
|
||||||
|
val threadDb: ThreadDatabase = mock(ThreadDatabase::class.java)
|
||||||
|
val groupDb: GroupDatabase = mock(GroupDatabase::class.java)
|
||||||
|
val storage: Storage = mock(Storage::class.java)
|
||||||
|
|
||||||
|
val recipient = mock(Recipient::class.java)
|
||||||
|
|
||||||
|
val groupRecord = mock(GroupRecord::class.java)
|
||||||
|
val optionalGroupRecord = Optional.of(groupRecord)
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `UI should show a list of times and an Off option`() = runTest {
|
||||||
|
val threadId = 1L
|
||||||
|
|
||||||
|
val expirationConfig = ExpirationConfiguration(
|
||||||
|
threadId = threadId,
|
||||||
|
expiryMode = ExpiryMode.AfterSend(12.hours.inWholeSeconds),
|
||||||
|
updatedTimestampMs = 0
|
||||||
|
)
|
||||||
|
whenever(threadDb.getRecipientForThreadId(Mockito.anyLong())).thenReturn(recipient)
|
||||||
|
whenever(storage.getExpirationConfiguration(Mockito.anyLong())).thenReturn(expirationConfig)
|
||||||
|
|
||||||
|
val address = Address.fromSerialized("${CLOSED_GROUP_PREFIX}94198734289")
|
||||||
|
|
||||||
|
whenever(recipient.isClosedGroupRecipient).thenReturn(true)
|
||||||
|
whenever(recipient.address).thenReturn(address)
|
||||||
|
|
||||||
|
whenever(groupDb.getGroup(Mockito.anyString())).thenReturn(optionalGroupRecord)
|
||||||
|
|
||||||
|
val viewModel = createViewModel()
|
||||||
|
|
||||||
|
advanceUntilIdle()
|
||||||
|
|
||||||
|
val state = viewModel.state.value
|
||||||
|
|
||||||
|
MatcherAssert.assertThat(
|
||||||
|
state.isGroup,
|
||||||
|
CoreMatchers.equalTo(true)
|
||||||
|
)
|
||||||
|
|
||||||
|
MatcherAssert.assertThat(
|
||||||
|
viewModel.uiState.value.cards.count(),
|
||||||
|
CoreMatchers.equalTo(1)
|
||||||
|
)
|
||||||
|
|
||||||
|
MatcherAssert.assertThat(
|
||||||
|
viewModel.uiState.value.cards[0].options.count(),
|
||||||
|
CoreMatchers.equalTo(6)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createViewModel(isNewConfigEnabled: Boolean = true) = ExpirationSettingsViewModel(
|
||||||
|
1L,
|
||||||
|
application,
|
||||||
|
textSecurePreferences,
|
||||||
|
messageExpirationManager,
|
||||||
|
threadDb,
|
||||||
|
groupDb,
|
||||||
|
storage,
|
||||||
|
isNewConfigEnabled
|
||||||
|
)
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user