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:
Sedat Qaja
2026-10-19 09:05:35 +02:00
parent 9cb72c74aa
commit db839dc5c3
25 changed files with 91 additions and 73 deletions
+2
View File
@@ -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
+1 -1
View File
@@ -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 }}
+12
View File
@@ -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
@@ -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
}
@@ -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
@@ -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,
@@ -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
}
}
@@ -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(),
@@ -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,
@@ -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(),
@@ -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,
@@ -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(),
@@ -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,
@@ -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
}
@@ -63,9 +63,9 @@ class HealthChatProfileSectionPresenter(
memberProfile
}
fun handleEvents(event: HealthChatProfileSectionEvents) {
fun handleEvents(event: HealthChatProfileSectionEvent) {
when (event) {
HealthChatProfileSectionEvents.Retry -> retryTrigger++
HealthChatProfileSectionEvent.Retry -> retryTrigger++
}
}
@@ -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,
)
@@ -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,
@@ -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)
}
@@ -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)),
@@ -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,
@@ -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)
}
@@ -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,
@@ -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)
@@ -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)
}
}
@@ -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 = {},