mirror of
https://github.com/Novaloop-AG/element-x-android.git
synced 2026-10-31 08:15:02 +00:00
Adapt HealthChat to upstream v26.10.0
Follow-ups needed after rebasing the HealthChat series from v26.07.0 onto v26.10.0 that span several of the replayed commits: - Rename the five PreviewParameterProvider subclasses in features/healthchat to the *PreviewParam suffix now enforced by Konsist. - Rename MemberDirectoryEvents and HealthChatProfileSectionEvents to the *Event suffix used by every upstream sealed interface. - Import FakeStartDMAction from features/startchat/test, where it lives now. - ListItem lost its headlineContent parameter in favour of content. - HomeView gained onAddAccountClick and onSwitchToAccount, and onRoomClick now carries an EventId; update HomeViewBackHandlingTest accordingly. - Restore upstream's codecov-action pin (v7.1.1) that the CI squash carried back to v7.0.0. - Gate the new pr-checks.yml on the upstream repository: this fork does not use the PR- label convention. - Add the 26.10.0 section to HEALTHCHAT_CHANGES.md.
This commit is contained in:
@@ -10,6 +10,8 @@ permissions: {}
|
||||
|
||||
jobs:
|
||||
pr-checks:
|
||||
# Upstream-only: this fork does not use the PR- label convention.
|
||||
if: github.repository == 'element-hq/element-x-android'
|
||||
name: PR Checks
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 5
|
||||
|
||||
@@ -152,7 +152,7 @@ jobs:
|
||||
if: env.CODECOV_TOKEN != ''
|
||||
env:
|
||||
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
||||
uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0
|
||||
uses: codecov/codecov-action@303a32d7a59b442fa8d48b6a1cc6825c09c847a5 # v7.1.1
|
||||
with:
|
||||
fail_ci_if_error: true
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
|
||||
@@ -6,6 +6,18 @@ top section matching the released version — the heading must have the exact
|
||||
form `## Changes in <version> (<date>)` and the whole section must stay under
|
||||
500 characters. See `.claude/skills/releasing-healthchat/SKILL.md`.
|
||||
|
||||
## Changes in 26.10.0 (2026-10-19)
|
||||
|
||||
🎉 Neue Funktionen
|
||||
|
||||
- Nachrichtensuche: Chats durchsuchen, auch in verschlüsselten Räumen.
|
||||
- Umfragen mit Mehrfachauswahl.
|
||||
|
||||
🙌 Verbesserungen
|
||||
|
||||
- Basis aktualisiert auf Element X 26.10.0 mit zahlreichen Fehlerbehebungen.
|
||||
- Einstellungen neu gegliedert in Konto- und App-Einstellungen.
|
||||
|
||||
## Changes in 26.08.0 (2026-08-06)
|
||||
|
||||
🎉 Neue Funktionen
|
||||
|
||||
+5
-5
@@ -10,17 +10,17 @@ package io.element.android.features.healthchat.api.directory
|
||||
|
||||
import io.element.android.libraries.matrix.api.user.MatrixUser
|
||||
|
||||
sealed interface MemberDirectoryEvents {
|
||||
sealed interface MemberDirectoryEvent {
|
||||
/**
|
||||
* Fired by the view on first composition. The tab presenter runs from app start (it is
|
||||
* composed by the Home presenter), so the first fetch is deferred until the tab is shown.
|
||||
*/
|
||||
data object LoadIfNeeded : MemberDirectoryEvents
|
||||
data object LoadIfNeeded : MemberDirectoryEvent
|
||||
|
||||
/** Header refresh and "try again" — one load path. */
|
||||
data object Refresh : MemberDirectoryEvents
|
||||
data object Refresh : MemberDirectoryEvent
|
||||
|
||||
data class StartDm(val matrixUser: MatrixUser) : MemberDirectoryEvents
|
||||
data class StartDm(val matrixUser: MatrixUser) : MemberDirectoryEvent
|
||||
|
||||
data object ClearStartDmState : MemberDirectoryEvents
|
||||
data object ClearStartDmState : MemberDirectoryEvent
|
||||
}
|
||||
+1
-1
@@ -18,7 +18,7 @@ data class MemberDirectoryState(
|
||||
val searchQuery: TextFieldState,
|
||||
val directory: AsyncData<HealthChatMemberDirectory>,
|
||||
val startDmActionState: AsyncAction<RoomId>,
|
||||
val eventSink: (MemberDirectoryEvents) -> Unit,
|
||||
val eventSink: (MemberDirectoryEvent) -> Unit,
|
||||
) {
|
||||
/** Unknown-while-loading counts as approved so no banner flashes during the first load. */
|
||||
val isApproved: Boolean
|
||||
|
||||
+1
-1
@@ -25,7 +25,7 @@ fun aMemberDirectoryState(
|
||||
searchQuery: TextFieldState = TextFieldState(),
|
||||
directory: AsyncData<HealthChatMemberDirectory> = AsyncData.Success(aMemberDirectory()),
|
||||
startDmActionState: AsyncAction<RoomId> = AsyncAction.Uninitialized,
|
||||
eventSink: (MemberDirectoryEvents) -> Unit = {},
|
||||
eventSink: (MemberDirectoryEvent) -> Unit = {},
|
||||
) = MemberDirectoryState(
|
||||
searchQuery = searchQuery,
|
||||
directory = directory,
|
||||
|
||||
+6
-6
@@ -21,7 +21,7 @@ import androidx.compose.runtime.setValue
|
||||
import dev.zacsweers.metro.Inject
|
||||
import io.element.android.features.healthchat.api.HealthChatDirectoryService
|
||||
import io.element.android.features.healthchat.api.HealthChatMemberDirectory
|
||||
import io.element.android.features.healthchat.api.directory.MemberDirectoryEvents
|
||||
import io.element.android.features.healthchat.api.directory.MemberDirectoryEvent
|
||||
import io.element.android.features.healthchat.api.directory.MemberDirectoryState
|
||||
import io.element.android.features.startchat.api.StartDMAction
|
||||
import io.element.android.libraries.architecture.AsyncAction
|
||||
@@ -62,22 +62,22 @@ class MemberDirectoryPresenter(
|
||||
)
|
||||
}
|
||||
|
||||
fun handleEvents(event: MemberDirectoryEvents) {
|
||||
fun handleEvents(event: MemberDirectoryEvent) {
|
||||
when (event) {
|
||||
MemberDirectoryEvents.LoadIfNeeded -> {
|
||||
MemberDirectoryEvent.LoadIfNeeded -> {
|
||||
hasBeenRequested = true
|
||||
}
|
||||
MemberDirectoryEvents.Refresh -> {
|
||||
MemberDirectoryEvent.Refresh -> {
|
||||
loadTrigger++
|
||||
}
|
||||
is MemberDirectoryEvents.StartDm -> coroutineScope.launch {
|
||||
is MemberDirectoryEvent.StartDm -> coroutineScope.launch {
|
||||
startDMAction.execute(
|
||||
matrixUser = event.matrixUser,
|
||||
createIfDmDoesNotExist = startDmActionState.value is AsyncAction.Confirming,
|
||||
actionState = startDmActionState,
|
||||
)
|
||||
}
|
||||
MemberDirectoryEvents.ClearStartDmState -> {
|
||||
MemberDirectoryEvent.ClearStartDmState -> {
|
||||
startDmActionState.value = AsyncAction.Uninitialized
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -15,7 +15,7 @@ import io.element.android.features.healthchat.api.directory.aMemberDirectory
|
||||
import io.element.android.features.healthchat.api.directory.aMemberDirectoryState
|
||||
import io.element.android.libraries.architecture.AsyncData
|
||||
|
||||
open class MemberDirectoryStateProvider : PreviewParameterProvider<MemberDirectoryState> {
|
||||
open class MemberDirectoryStatePreviewParam : PreviewParameterProvider<MemberDirectoryState> {
|
||||
override val values: Sequence<MemberDirectoryState>
|
||||
get() = sequenceOf(
|
||||
aMemberDirectoryState(),
|
||||
+9
-9
@@ -27,7 +27,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.unit.dp
|
||||
import io.element.android.compound.theme.ElementTheme
|
||||
import io.element.android.features.healthchat.api.HealthChatStrings
|
||||
import io.element.android.features.healthchat.api.directory.MemberDirectoryEvents
|
||||
import io.element.android.features.healthchat.api.directory.MemberDirectoryEvent
|
||||
import io.element.android.features.healthchat.api.directory.MemberDirectoryState
|
||||
import io.element.android.features.healthchat.api.toMatrixUser
|
||||
import io.element.android.features.healthchat.impl.directory.components.MemberCard
|
||||
@@ -63,7 +63,7 @@ fun MemberDirectoryView(
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
LaunchedEffect(Unit) {
|
||||
state.eventSink(MemberDirectoryEvents.LoadIfNeeded)
|
||||
state.eventSink(MemberDirectoryEvent.LoadIfNeeded)
|
||||
}
|
||||
Box(modifier = modifier.fillMaxSize()) {
|
||||
when (val directory = state.directory) {
|
||||
@@ -84,7 +84,7 @@ fun MemberDirectoryView(
|
||||
}
|
||||
is AsyncData.Failure -> MemberDirectoryErrorContent(
|
||||
message = directory.error.healthChatUserMessage(),
|
||||
onRetry = { state.eventSink(MemberDirectoryEvents.Refresh) },
|
||||
onRetry = { state.eventSink(MemberDirectoryEvent.Refresh) },
|
||||
)
|
||||
is AsyncData.Success -> MemberDirectoryContent(
|
||||
state = state,
|
||||
@@ -107,17 +107,17 @@ fun MemberDirectoryView(
|
||||
onSuccess = onOpenDm,
|
||||
errorMessage = { stringResource(HealthChatStrings.healthchat_screen_member_directory_error_starting_chat) },
|
||||
onRetry = null,
|
||||
onErrorDismiss = { state.eventSink(MemberDirectoryEvents.ClearStartDmState) },
|
||||
onErrorDismiss = { state.eventSink(MemberDirectoryEvent.ClearStartDmState) },
|
||||
confirmationDialog = { data ->
|
||||
if (data is ConfirmingStartDmWithMatrixUser) {
|
||||
CreateDmConfirmationBottomSheet(
|
||||
matrixUser = data.matrixUser,
|
||||
isUserIdentityUnknown = data.isUserIdentityUnknown,
|
||||
onSendInvite = {
|
||||
state.eventSink(MemberDirectoryEvents.StartDm(data.matrixUser))
|
||||
state.eventSink(MemberDirectoryEvent.StartDm(data.matrixUser))
|
||||
},
|
||||
onDismiss = {
|
||||
state.eventSink(MemberDirectoryEvents.ClearStartDmState)
|
||||
state.eventSink(MemberDirectoryEvent.ClearStartDmState)
|
||||
},
|
||||
)
|
||||
}
|
||||
@@ -154,7 +154,7 @@ private fun MemberDirectoryContent(
|
||||
onMyProfileClick = onMyProfileClick,
|
||||
onEditProfileClick = onEditProfileClick,
|
||||
onInviteClick = if (state.isApproved) onInviteClick else null,
|
||||
onRefreshClick = { state.eventSink(MemberDirectoryEvents.Refresh) },
|
||||
onRefreshClick = { state.eventSink(MemberDirectoryEvent.Refresh) },
|
||||
)
|
||||
}
|
||||
if (!state.isApproved) {
|
||||
@@ -196,7 +196,7 @@ private fun MemberDirectoryContent(
|
||||
MemberCard(
|
||||
member = member,
|
||||
onClick = onMemberClick?.let { callback -> { callback(member.userId) } },
|
||||
onStartChatClick = { state.eventSink(MemberDirectoryEvents.StartDm(member.toMatrixUser())) },
|
||||
onStartChatClick = { state.eventSink(MemberDirectoryEvent.StartDm(member.toMatrixUser())) },
|
||||
modifier = Modifier.padding(horizontal = 16.dp, vertical = 4.dp),
|
||||
)
|
||||
}
|
||||
@@ -219,7 +219,7 @@ private fun FullScreenLoading(
|
||||
@PreviewsDayNight
|
||||
@Composable
|
||||
internal fun MemberDirectoryViewPreview(
|
||||
@PreviewParameter(MemberDirectoryStateProvider::class) state: MemberDirectoryState
|
||||
@PreviewParameter(MemberDirectoryStatePreviewParam::class) state: MemberDirectoryState
|
||||
) = ElementPreview {
|
||||
MemberDirectoryView(
|
||||
state = state,
|
||||
|
||||
+1
-1
@@ -12,7 +12,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||
import io.element.android.features.healthchat.api.HealthChatStrings
|
||||
import io.element.android.libraries.architecture.AsyncAction
|
||||
|
||||
open class HealthChatInviteStateProvider : PreviewParameterProvider<HealthChatInviteState> {
|
||||
open class HealthChatInviteStatePreviewParam : PreviewParameterProvider<HealthChatInviteState> {
|
||||
override val values: Sequence<HealthChatInviteState>
|
||||
get() = sequenceOf(
|
||||
aHealthChatInviteState(),
|
||||
+1
-1
@@ -136,7 +136,7 @@ private fun HealthChatFieldError.text(): String {
|
||||
@PreviewsDayNight
|
||||
@Composable
|
||||
internal fun HealthChatInviteViewPreview(
|
||||
@PreviewParameter(HealthChatInviteStateProvider::class) state: HealthChatInviteState
|
||||
@PreviewParameter(HealthChatInviteStatePreviewParam::class) state: HealthChatInviteState
|
||||
) = ElementPreview {
|
||||
HealthChatInviteView(
|
||||
state = state,
|
||||
|
||||
+1
-1
@@ -24,7 +24,7 @@ import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.collections.immutable.persistentMapOf
|
||||
import kotlinx.collections.immutable.toImmutableMap
|
||||
|
||||
open class EditHealthChatProfileStateProvider : PreviewParameterProvider<EditHealthChatProfileState> {
|
||||
open class EditHealthChatProfileStatePreviewParam : PreviewParameterProvider<EditHealthChatProfileState> {
|
||||
override val values: Sequence<EditHealthChatProfileState>
|
||||
get() = sequenceOf(
|
||||
anEditHealthChatProfileState(),
|
||||
+1
-1
@@ -284,7 +284,7 @@ private fun ProfileFormField(
|
||||
@PreviewsDayNight
|
||||
@Composable
|
||||
internal fun EditHealthChatProfileViewPreview(
|
||||
@PreviewParameter(EditHealthChatProfileStateProvider::class) state: EditHealthChatProfileState
|
||||
@PreviewParameter(EditHealthChatProfileStatePreviewParam::class) state: EditHealthChatProfileState
|
||||
) = ElementPreview {
|
||||
EditHealthChatProfileView(
|
||||
state = state,
|
||||
|
||||
+2
-2
@@ -8,6 +8,6 @@
|
||||
|
||||
package io.element.android.features.healthchat.impl.profile.section
|
||||
|
||||
sealed interface HealthChatProfileSectionEvents {
|
||||
data object Retry : HealthChatProfileSectionEvents
|
||||
sealed interface HealthChatProfileSectionEvent {
|
||||
data object Retry : HealthChatProfileSectionEvent
|
||||
}
|
||||
+2
-2
@@ -63,9 +63,9 @@ class HealthChatProfileSectionPresenter(
|
||||
memberProfile
|
||||
}
|
||||
|
||||
fun handleEvents(event: HealthChatProfileSectionEvents) {
|
||||
fun handleEvents(event: HealthChatProfileSectionEvent) {
|
||||
when (event) {
|
||||
HealthChatProfileSectionEvents.Retry -> retryTrigger++
|
||||
HealthChatProfileSectionEvent.Retry -> retryTrigger++
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -13,5 +13,5 @@ import io.element.android.libraries.architecture.AsyncData
|
||||
|
||||
data class HealthChatProfileSectionState(
|
||||
val profile: AsyncData<HealthChatMember>,
|
||||
val eventSink: (HealthChatProfileSectionEvents) -> Unit,
|
||||
val eventSink: (HealthChatProfileSectionEvent) -> Unit,
|
||||
)
|
||||
|
||||
+2
-2
@@ -14,7 +14,7 @@ import io.element.android.features.healthchat.api.HealthChatMember
|
||||
import io.element.android.libraries.architecture.AsyncData
|
||||
import io.element.android.libraries.matrix.api.core.UserId
|
||||
|
||||
open class HealthChatProfileSectionStateProvider : PreviewParameterProvider<HealthChatProfileSectionState> {
|
||||
open class HealthChatProfileSectionStatePreviewParam : PreviewParameterProvider<HealthChatProfileSectionState> {
|
||||
override val values: Sequence<HealthChatProfileSectionState>
|
||||
get() = sequenceOf(
|
||||
aHealthChatProfileSectionState(profile = AsyncData.Success(aProfileSectionMember())),
|
||||
@@ -45,7 +45,7 @@ open class HealthChatProfileSectionStateProvider : PreviewParameterProvider<Heal
|
||||
|
||||
fun aHealthChatProfileSectionState(
|
||||
profile: AsyncData<HealthChatMember> = AsyncData.Uninitialized,
|
||||
eventSink: (HealthChatProfileSectionEvents) -> Unit = {},
|
||||
eventSink: (HealthChatProfileSectionEvent) -> Unit = {},
|
||||
) = HealthChatProfileSectionState(
|
||||
profile = profile,
|
||||
eventSink = eventSink,
|
||||
+4
-4
@@ -56,7 +56,7 @@ fun HealthChatProfileSectionView(
|
||||
is HealthChatApiException.NotFound -> Unit
|
||||
is HealthChatApiException.NotApproved -> NotApprovedHint(modifier = modifier)
|
||||
else -> LoadingErrorRow(
|
||||
onRetry = { state.eventSink(HealthChatProfileSectionEvents.Retry) },
|
||||
onRetry = { state.eventSink(HealthChatProfileSectionEvent.Retry) },
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
@@ -147,7 +147,7 @@ private fun ProfileFieldRow(
|
||||
onClick: (() -> Unit)? = null,
|
||||
) {
|
||||
ListItem(
|
||||
headlineContent = { Text(value) },
|
||||
content = { Text(value) },
|
||||
supportingContent = { Text(stringResource(labelRes)) },
|
||||
leadingContent = ListItemContent.Icon(IconSource.Vector(icon)),
|
||||
onClick = onClick,
|
||||
@@ -175,7 +175,7 @@ private fun LoadingErrorRow(
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
ListItem(
|
||||
headlineContent = { Text(stringResource(HealthChatStrings.healthchat_profile_error_loading)) },
|
||||
content = { Text(stringResource(HealthChatStrings.healthchat_profile_error_loading)) },
|
||||
leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.Error())),
|
||||
trailingContent = ListItemContent.Custom {
|
||||
TextButton(
|
||||
@@ -206,7 +206,7 @@ private fun String.withHttpScheme(): String {
|
||||
@PreviewsDayNight
|
||||
@Composable
|
||||
internal fun HealthChatProfileSectionViewPreview(
|
||||
@PreviewParameter(HealthChatProfileSectionStateProvider::class) state: HealthChatProfileSectionState
|
||||
@PreviewParameter(HealthChatProfileSectionStatePreviewParam::class) state: HealthChatProfileSectionState
|
||||
) = ElementPreview {
|
||||
HealthChatProfileSectionView(state = state)
|
||||
}
|
||||
|
||||
+1
-1
@@ -11,7 +11,7 @@ package io.element.android.features.healthchat.impl.status
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
|
||||
open class HealthChatAccountStatusStateProvider : PreviewParameterProvider<HealthChatAccountStatusState> {
|
||||
open class HealthChatAccountStatusStatePreviewParam : PreviewParameterProvider<HealthChatAccountStatusState> {
|
||||
override val values: Sequence<HealthChatAccountStatusState>
|
||||
get() = sequenceOf(
|
||||
HealthChatAccountStatusState(status = HealthChatAccountStatus.PendingApproval(serverMessage = null)),
|
||||
+1
-1
@@ -74,7 +74,7 @@ fun HealthChatAccountStatusView(
|
||||
@PreviewsDayNight
|
||||
@Composable
|
||||
internal fun HealthChatAccountStatusViewPreview(
|
||||
@PreviewParameter(HealthChatAccountStatusStateProvider::class) state: HealthChatAccountStatusState
|
||||
@PreviewParameter(HealthChatAccountStatusStatePreviewParam::class) state: HealthChatAccountStatusState
|
||||
) = ElementPreview {
|
||||
HealthChatAccountStatusView(
|
||||
state = state,
|
||||
|
||||
+9
-9
@@ -15,11 +15,11 @@ import io.element.android.features.healthchat.api.HealthChatApiException
|
||||
import io.element.android.features.healthchat.api.HealthChatDirectoryService
|
||||
import io.element.android.features.healthchat.api.HealthChatMember
|
||||
import io.element.android.features.healthchat.api.HealthChatMemberDirectory
|
||||
import io.element.android.features.healthchat.api.directory.MemberDirectoryEvents
|
||||
import io.element.android.features.healthchat.api.directory.MemberDirectoryEvent
|
||||
import io.element.android.features.healthchat.test.FakeHealthChatDirectoryService
|
||||
import io.element.android.features.healthchat.test.aHealthChatMember
|
||||
import io.element.android.features.invitepeople.test.FakeStartDMAction
|
||||
import io.element.android.features.startchat.api.StartDMAction
|
||||
import io.element.android.features.startchat.test.FakeStartDMAction
|
||||
import io.element.android.libraries.architecture.AsyncAction
|
||||
import io.element.android.libraries.architecture.AsyncData
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
@@ -52,7 +52,7 @@ class MemberDirectoryPresenterTest {
|
||||
val initialState = awaitItem()
|
||||
assertThat(initialState.directory).isEqualTo(AsyncData.Uninitialized)
|
||||
searchLambda.assertions().isNeverCalled()
|
||||
initialState.eventSink(MemberDirectoryEvents.LoadIfNeeded)
|
||||
initialState.eventSink(MemberDirectoryEvent.LoadIfNeeded)
|
||||
val loadedState = consumeItemsUntilPredicate { it.directory is AsyncData.Success }.last()
|
||||
assertThat(loadedState.directory.dataOrNull()).isEqualTo(aDirectory())
|
||||
searchLambda.assertions().isCalledOnce()
|
||||
@@ -72,7 +72,7 @@ class MemberDirectoryPresenterTest {
|
||||
)
|
||||
presenter.test {
|
||||
val initialState = awaitItem()
|
||||
initialState.eventSink(MemberDirectoryEvents.LoadIfNeeded)
|
||||
initialState.eventSink(MemberDirectoryEvent.LoadIfNeeded)
|
||||
val loadedState = consumeItemsUntilPredicate { it.directory is AsyncData.Success }.last()
|
||||
// Typing restarts the effect: intermediate values must be debounced away.
|
||||
loadedState.searchQuery.setTextAndPlaceCursorAtEnd("M")
|
||||
@@ -100,11 +100,11 @@ class MemberDirectoryPresenterTest {
|
||||
)
|
||||
presenter.test {
|
||||
val initialState = awaitItem()
|
||||
initialState.eventSink(MemberDirectoryEvents.LoadIfNeeded)
|
||||
initialState.eventSink(MemberDirectoryEvent.LoadIfNeeded)
|
||||
val failedState = consumeItemsUntilPredicate { it.directory is AsyncData.Failure }.last()
|
||||
// No stale fallback.
|
||||
assertThat((failedState.directory as AsyncData.Failure).prevData).isNull()
|
||||
failedState.eventSink(MemberDirectoryEvents.Refresh)
|
||||
failedState.eventSink(MemberDirectoryEvent.Refresh)
|
||||
val loadedState = consumeItemsUntilPredicate { it.directory is AsyncData.Success }.last()
|
||||
assertThat(loadedState.directory.dataOrNull()).isEqualTo(aDirectory())
|
||||
}
|
||||
@@ -127,7 +127,7 @@ class MemberDirectoryPresenterTest {
|
||||
)
|
||||
presenter.test {
|
||||
val initialState = awaitItem()
|
||||
initialState.eventSink(MemberDirectoryEvents.LoadIfNeeded)
|
||||
initialState.eventSink(MemberDirectoryEvent.LoadIfNeeded)
|
||||
val state = consumeItemsUntilPredicate { it.directory is AsyncData.Success }.last()
|
||||
assertThat(state.isApproved).isFalse()
|
||||
assertThat(state.pendingMessage).isEqualTo("Ihr Konto wird derzeit geprüft.")
|
||||
@@ -149,11 +149,11 @@ class MemberDirectoryPresenterTest {
|
||||
)
|
||||
presenter.test {
|
||||
val initialState = awaitItem()
|
||||
initialState.eventSink(MemberDirectoryEvents.StartDm(MatrixUser(A_USER_ID)))
|
||||
initialState.eventSink(MemberDirectoryEvent.StartDm(MatrixUser(A_USER_ID)))
|
||||
val state = consumeItemsUntilPredicate { it.startDmActionState is AsyncAction.Success }.last()
|
||||
assertThat((state.startDmActionState as AsyncAction.Success).data).isEqualTo(A_ROOM_ID)
|
||||
executeLambda.assertions().isCalledOnce()
|
||||
state.eventSink(MemberDirectoryEvents.ClearStartDmState)
|
||||
state.eventSink(MemberDirectoryEvent.ClearStartDmState)
|
||||
val clearedState = consumeItemsUntilPredicate { it.startDmActionState is AsyncAction.Uninitialized }.last()
|
||||
assertThat(clearedState.startDmActionState).isEqualTo(AsyncAction.Uninitialized)
|
||||
}
|
||||
|
||||
+12
-12
@@ -18,7 +18,7 @@ import androidx.compose.ui.test.onNodeWithText
|
||||
import androidx.compose.ui.test.performClick
|
||||
import androidx.compose.ui.test.v2.runAndroidComposeUiTest
|
||||
import io.element.android.features.healthchat.api.HealthChatApiException
|
||||
import io.element.android.features.healthchat.api.directory.MemberDirectoryEvents
|
||||
import io.element.android.features.healthchat.api.directory.MemberDirectoryEvent
|
||||
import io.element.android.features.healthchat.api.directory.MemberDirectoryState
|
||||
import io.element.android.features.healthchat.api.directory.aDirectoryMember
|
||||
import io.element.android.features.healthchat.api.directory.aMemberDirectory
|
||||
@@ -37,19 +37,19 @@ import org.junit.Test
|
||||
class MemberDirectoryViewTest : RobolectricTest() {
|
||||
@Test
|
||||
fun `on composition LoadIfNeeded is emitted`() = runAndroidComposeUiTest<ComponentActivity> {
|
||||
val eventsRecorder = EventsRecorder<MemberDirectoryEvents>()
|
||||
val eventsRecorder = EventsRecorder<MemberDirectoryEvent>()
|
||||
setMemberDirectoryView(
|
||||
state = aMemberDirectoryState(
|
||||
directory = AsyncData.Uninitialized,
|
||||
eventSink = eventsRecorder,
|
||||
),
|
||||
)
|
||||
eventsRecorder.assertSingle(MemberDirectoryEvents.LoadIfNeeded)
|
||||
eventsRecorder.assertSingle(MemberDirectoryEvent.LoadIfNeeded)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `clicking retry on the error state emits Refresh`() = runAndroidComposeUiTest<ComponentActivity> {
|
||||
val eventsRecorder = EventsRecorder<MemberDirectoryEvents>()
|
||||
val eventsRecorder = EventsRecorder<MemberDirectoryEvent>()
|
||||
setMemberDirectoryView(
|
||||
state = aMemberDirectoryState(
|
||||
directory = AsyncData.Failure(HealthChatApiException.Http(500, "Fehler beim Laden der Benutzer")),
|
||||
@@ -61,8 +61,8 @@ class MemberDirectoryViewTest : RobolectricTest() {
|
||||
clickOn(CommonStrings.action_retry)
|
||||
eventsRecorder.assertList(
|
||||
listOf(
|
||||
MemberDirectoryEvents.LoadIfNeeded,
|
||||
MemberDirectoryEvents.Refresh,
|
||||
MemberDirectoryEvent.LoadIfNeeded,
|
||||
MemberDirectoryEvent.Refresh,
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -70,7 +70,7 @@ class MemberDirectoryViewTest : RobolectricTest() {
|
||||
@Test
|
||||
fun `clicking start chat emits StartDm with the member as MatrixUser`() = runAndroidComposeUiTest<ComponentActivity> {
|
||||
val member = aDirectoryMember()
|
||||
val eventsRecorder = EventsRecorder<MemberDirectoryEvents>()
|
||||
val eventsRecorder = EventsRecorder<MemberDirectoryEvent>()
|
||||
setMemberDirectoryView(
|
||||
state = aMemberDirectoryState(
|
||||
directory = AsyncData.Success(aMemberDirectory(members = listOf(member))),
|
||||
@@ -82,8 +82,8 @@ class MemberDirectoryViewTest : RobolectricTest() {
|
||||
.performClick()
|
||||
eventsRecorder.assertList(
|
||||
listOf(
|
||||
MemberDirectoryEvents.LoadIfNeeded,
|
||||
MemberDirectoryEvents.StartDm(member.toMatrixUser()),
|
||||
MemberDirectoryEvent.LoadIfNeeded,
|
||||
MemberDirectoryEvent.StartDm(member.toMatrixUser()),
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -91,7 +91,7 @@ class MemberDirectoryViewTest : RobolectricTest() {
|
||||
@Test
|
||||
fun `clicking a member card opens the profile`() = runAndroidComposeUiTest<ComponentActivity> {
|
||||
val member = aDirectoryMember()
|
||||
val eventsRecorder = EventsRecorder<MemberDirectoryEvents>(expectEvents = true)
|
||||
val eventsRecorder = EventsRecorder<MemberDirectoryEvent>(expectEvents = true)
|
||||
ensureCalledOnceWithParam(member.userId) { callback ->
|
||||
setMemberDirectoryView(
|
||||
state = aMemberDirectoryState(
|
||||
@@ -106,7 +106,7 @@ class MemberDirectoryViewTest : RobolectricTest() {
|
||||
|
||||
@Test
|
||||
fun `an unapproved account shows the pending banner and no search field`() = runAndroidComposeUiTest<ComponentActivity> {
|
||||
val eventsRecorder = EventsRecorder<MemberDirectoryEvents>(expectEvents = true)
|
||||
val eventsRecorder = EventsRecorder<MemberDirectoryEvent>(expectEvents = true)
|
||||
setMemberDirectoryView(
|
||||
state = aMemberDirectoryState(
|
||||
directory = AsyncData.Success(
|
||||
@@ -127,7 +127,7 @@ class MemberDirectoryViewTest : RobolectricTest() {
|
||||
|
||||
@Test
|
||||
fun `hidden affordances are not rendered when their callbacks are null`() = runAndroidComposeUiTest<ComponentActivity> {
|
||||
val eventsRecorder = EventsRecorder<MemberDirectoryEvents>(expectEvents = true)
|
||||
val eventsRecorder = EventsRecorder<MemberDirectoryEvent>(expectEvents = true)
|
||||
setMemberDirectoryView(
|
||||
state = aMemberDirectoryState(eventSink = eventsRecorder),
|
||||
onMyProfileClick = null,
|
||||
|
||||
+1
-1
@@ -106,7 +106,7 @@ class HealthChatProfileSectionPresenterTest {
|
||||
val presenter = createPresenter(isCurrentUser = false, directoryService = directoryService)
|
||||
presenter.test {
|
||||
val failedState = consumeItemsUntilPredicate { it.profile is AsyncData.Failure }.last()
|
||||
failedState.eventSink(HealthChatProfileSectionEvents.Retry)
|
||||
failedState.eventSink(HealthChatProfileSectionEvent.Retry)
|
||||
val state = consumeItemsUntilPredicate { it.profile is AsyncData.Success }.last()
|
||||
assertThat(state.profile).isEqualTo(AsyncData.Success(member))
|
||||
assertThat(callCount).isEqualTo(2)
|
||||
|
||||
+8
-8
@@ -30,7 +30,7 @@ import org.robolectric.Shadows.shadowOf
|
||||
class HealthChatProfileSectionViewTest : RobolectricTest() {
|
||||
@Test
|
||||
fun `a loaded profile renders its fields with values`() = runAndroidComposeUiTest<ComponentActivity> {
|
||||
val eventsRecorder = EventsRecorder<HealthChatProfileSectionEvents>(expectEvents = false)
|
||||
val eventsRecorder = EventsRecorder<HealthChatProfileSectionEvent>(expectEvents = false)
|
||||
setContent {
|
||||
HealthChatProfileSectionView(
|
||||
state = aHealthChatProfileSectionState(
|
||||
@@ -50,7 +50,7 @@ class HealthChatProfileSectionViewTest : RobolectricTest() {
|
||||
|
||||
@Test
|
||||
fun `empty fields are omitted`() = runAndroidComposeUiTest<ComponentActivity> {
|
||||
val eventsRecorder = EventsRecorder<HealthChatProfileSectionEvents>(expectEvents = false)
|
||||
val eventsRecorder = EventsRecorder<HealthChatProfileSectionEvent>(expectEvents = false)
|
||||
setContent {
|
||||
HealthChatProfileSectionView(
|
||||
state = aHealthChatProfileSectionState(
|
||||
@@ -77,7 +77,7 @@ class HealthChatProfileSectionViewTest : RobolectricTest() {
|
||||
|
||||
@Test
|
||||
fun `clicking the email row opens a mailto intent`() = runAndroidComposeUiTest<ComponentActivity> {
|
||||
val eventsRecorder = EventsRecorder<HealthChatProfileSectionEvents>(expectEvents = false)
|
||||
val eventsRecorder = EventsRecorder<HealthChatProfileSectionEvent>(expectEvents = false)
|
||||
setContent {
|
||||
HealthChatProfileSectionView(
|
||||
state = aHealthChatProfileSectionState(
|
||||
@@ -94,7 +94,7 @@ class HealthChatProfileSectionViewTest : RobolectricTest() {
|
||||
|
||||
@Test
|
||||
fun `clicking the phone row opens a tel intent`() = runAndroidComposeUiTest<ComponentActivity> {
|
||||
val eventsRecorder = EventsRecorder<HealthChatProfileSectionEvents>(expectEvents = false)
|
||||
val eventsRecorder = EventsRecorder<HealthChatProfileSectionEvent>(expectEvents = false)
|
||||
setContent {
|
||||
HealthChatProfileSectionView(
|
||||
state = aHealthChatProfileSectionState(
|
||||
@@ -110,7 +110,7 @@ class HealthChatProfileSectionViewTest : RobolectricTest() {
|
||||
|
||||
@Test
|
||||
fun `a loading state renders nothing`() = runAndroidComposeUiTest<ComponentActivity> {
|
||||
val eventsRecorder = EventsRecorder<HealthChatProfileSectionEvents>(expectEvents = false)
|
||||
val eventsRecorder = EventsRecorder<HealthChatProfileSectionEvent>(expectEvents = false)
|
||||
setContent {
|
||||
HealthChatProfileSectionView(
|
||||
state = aHealthChatProfileSectionState(
|
||||
@@ -124,7 +124,7 @@ class HealthChatProfileSectionViewTest : RobolectricTest() {
|
||||
|
||||
@Test
|
||||
fun `a NotFound failure renders nothing`() = runAndroidComposeUiTest<ComponentActivity> {
|
||||
val eventsRecorder = EventsRecorder<HealthChatProfileSectionEvents>(expectEvents = false)
|
||||
val eventsRecorder = EventsRecorder<HealthChatProfileSectionEvent>(expectEvents = false)
|
||||
val activity = requireNotNull(activity)
|
||||
setContent {
|
||||
HealthChatProfileSectionView(
|
||||
@@ -139,7 +139,7 @@ class HealthChatProfileSectionViewTest : RobolectricTest() {
|
||||
|
||||
@Test
|
||||
fun `an error state shows a retry row which emits Retry`() = runAndroidComposeUiTest<ComponentActivity> {
|
||||
val eventsRecorder = EventsRecorder<HealthChatProfileSectionEvents>()
|
||||
val eventsRecorder = EventsRecorder<HealthChatProfileSectionEvent>()
|
||||
setContent {
|
||||
HealthChatProfileSectionView(
|
||||
state = aHealthChatProfileSectionState(
|
||||
@@ -149,6 +149,6 @@ class HealthChatProfileSectionViewTest : RobolectricTest() {
|
||||
)
|
||||
}
|
||||
clickOn(CommonStrings.action_retry)
|
||||
eventsRecorder.assertSingle(HealthChatProfileSectionEvents.Retry)
|
||||
eventsRecorder.assertSingle(HealthChatProfileSectionEvent.Retry)
|
||||
}
|
||||
}
|
||||
|
||||
+6
-2
@@ -28,8 +28,10 @@ class HomeViewBackHandlingTest : RobolectricTest() {
|
||||
currentHomeNavigationBarItem = HomeNavigationBarItem.Members,
|
||||
eventSink = eventsRecorder,
|
||||
),
|
||||
onRoomClick = {},
|
||||
onRoomClick = { _, _ -> },
|
||||
onSettingsClick = {},
|
||||
onAddAccountClick = {},
|
||||
onSwitchToAccount = {},
|
||||
onSetUpRecoveryClick = {},
|
||||
onConfirmRecoveryKeyClick = {},
|
||||
onStartChatClick = {},
|
||||
@@ -58,8 +60,10 @@ class HomeViewBackHandlingTest : RobolectricTest() {
|
||||
currentHomeNavigationBarItem = HomeNavigationBarItem.Members,
|
||||
eventSink = eventsRecorder,
|
||||
),
|
||||
onRoomClick = {},
|
||||
onRoomClick = { _, _ -> },
|
||||
onSettingsClick = {},
|
||||
onAddAccountClick = {},
|
||||
onSwitchToAccount = {},
|
||||
onSetUpRecoveryClick = {},
|
||||
onConfirmRecoveryKeyClick = {},
|
||||
onStartChatClick = {},
|
||||
|
||||
Reference in New Issue
Block a user