From c35fca3a1b8bdd661cf0ac7d15501b0c12a73159 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 26 Feb 2024 15:31:50 +0100 Subject: [PATCH 1/9] Add test on MediaViewerView --- libraries/mediaviewer/api/build.gradle.kts | 7 + .../api/viewer/MediaViewerStateProvider.kt | 4 +- .../api/viewer/MediaViewerViewTest.kt | 177 ++++++++++++++++++ 3 files changed, 187 insertions(+), 1 deletion(-) create mode 100644 libraries/mediaviewer/api/src/test/kotlin/io/element/android/libraries/mediaviewer/api/viewer/MediaViewerViewTest.kt diff --git a/libraries/mediaviewer/api/build.gradle.kts b/libraries/mediaviewer/api/build.gradle.kts index 2745302623..28590bfcfc 100644 --- a/libraries/mediaviewer/api/build.gradle.kts +++ b/libraries/mediaviewer/api/build.gradle.kts @@ -22,6 +22,11 @@ plugins { android { namespace = "io.element.android.libraries.mediaviewer.api" + testOptions { + unitTests { + isIncludeAndroidResources = true + } + } } anvil { @@ -60,6 +65,8 @@ dependencies { testImplementation(libs.test.turbine) testImplementation(libs.coroutines.core) testImplementation(libs.coroutines.test) + testImplementation(libs.androidx.compose.ui.test.junit) + testReleaseImplementation(libs.androidx.compose.ui.test.manifest) ksp(libs.showkase.processor) } diff --git a/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/viewer/MediaViewerStateProvider.kt b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/viewer/MediaViewerStateProvider.kt index a9d6e4fc98..d46fd64cc7 100644 --- a/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/viewer/MediaViewerStateProvider.kt +++ b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/viewer/MediaViewerStateProvider.kt @@ -87,6 +87,7 @@ fun aMediaViewerState( mediaInfo: MediaInfo = anImageInfo(), canDownload: Boolean = true, canShare: Boolean = true, + eventSink: (MediaViewerEvents) -> Unit = {}, ) = MediaViewerState( mediaInfo = mediaInfo, thumbnailSource = null, @@ -94,4 +95,5 @@ fun aMediaViewerState( snackbarMessage = null, canDownload = canDownload, canShare = canShare, -) {} + eventSink = eventSink, +) diff --git a/libraries/mediaviewer/api/src/test/kotlin/io/element/android/libraries/mediaviewer/api/viewer/MediaViewerViewTest.kt b/libraries/mediaviewer/api/src/test/kotlin/io/element/android/libraries/mediaviewer/api/viewer/MediaViewerViewTest.kt new file mode 100644 index 0000000000..1faeb0772e --- /dev/null +++ b/libraries/mediaviewer/api/src/test/kotlin/io/element/android/libraries/mediaviewer/api/viewer/MediaViewerViewTest.kt @@ -0,0 +1,177 @@ +/* + * Copyright (c) 2024 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.mediaviewer.api.viewer + +import android.net.Uri +import androidx.activity.ComponentActivity +import androidx.compose.ui.test.assertHasClickAction +import androidx.compose.ui.test.junit4.AndroidComposeTestRule +import androidx.compose.ui.test.junit4.createAndroidComposeRule +import androidx.compose.ui.test.onNodeWithContentDescription +import androidx.compose.ui.test.performClick +import androidx.compose.ui.test.performTouchInput +import androidx.compose.ui.test.swipeDown +import androidx.test.ext.junit.runners.AndroidJUnit4 +import io.element.android.libraries.architecture.AsyncData +import io.element.android.libraries.mediaviewer.api.local.LocalMedia +import io.element.android.libraries.mediaviewer.api.local.anImageInfo +import io.element.android.libraries.ui.strings.CommonStrings +import io.element.android.tests.testutils.EnsureNeverCalled +import io.element.android.tests.testutils.EventsRecorder +import io.element.android.tests.testutils.clickOn +import io.element.android.tests.testutils.ensureCalledOnce +import io.element.android.tests.testutils.pressBack +import org.junit.Ignore +import org.junit.Rule +import org.junit.Test +import org.junit.rules.TestRule +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4::class) +class MediaViewerViewTest { + @get:Rule val rule = createAndroidComposeRule() + + @Test + fun `clicking on back invokes expected callback`() { + val eventsRecorder = EventsRecorder(expectEvents = false) + ensureCalledOnce { callback -> + rule.setMediaViewerView( + aMediaViewerState( + eventSink = eventsRecorder + ), + onBackPressed = callback, + ) + rule.pressBack() + } + } + + @Test + fun `clicking on open emit expected Event`() { + testMenuAction(CommonStrings.action_open_with, MediaViewerEvents.OpenWith) + } + + @Test + fun `clicking on save emit expected Event`() { + testMenuAction(CommonStrings.action_save, MediaViewerEvents.SaveOnDisk) + } + + @Test + fun `clicking on share emit expected Event`() { + testMenuAction(CommonStrings.action_share, MediaViewerEvents.Share) + } + + private fun testMenuAction(contentDescriptionRes: Int, expectedEvent: MediaViewerEvents) { + val eventsRecorder = EventsRecorder() + rule.setMediaViewerView( + aMediaViewerState( + downloadedMedia = AsyncData.Success( + LocalMedia(Uri.EMPTY, anImageInfo()) + ), + mediaInfo = anImageInfo(), + eventSink = eventsRecorder + ), + ) + val contentDescription = rule.activity.getString(contentDescriptionRes) + rule.onNodeWithContentDescription(contentDescription).performClick() + eventsRecorder.assertSingle(expectedEvent) + } + + @Ignore("This test is not passing yet, maybe due to interaction with ZoomableAsyncImage?") + @Test + fun `clicking on image hides the overlay`() { + val eventsRecorder = EventsRecorder(expectEvents = false) + rule.setMediaViewerView( + aMediaViewerState( + downloadedMedia = AsyncData.Success( + LocalMedia(Uri.EMPTY, anImageInfo()) + ), + mediaInfo = anImageInfo(), + eventSink = eventsRecorder + ), + ) + // Ensure that the action are visible + val contentDescription = rule.activity.getString(CommonStrings.action_open_with) + rule.onNodeWithContentDescription(contentDescription).assertHasClickAction() + val imageContentDescription = rule.activity.getString(CommonStrings.common_image) + rule.onNodeWithContentDescription(imageContentDescription).performClick() + // assertHasNoClickAction does not work as expected (?) + // rule.onNodeWithContentDescription(contentDescription).assertHasNoClickAction() + rule.onNodeWithContentDescription(contentDescription).performClick() + // No emitted event + } + + @Ignore("This test is not passing yet, maybe due to interaction with ZoomableAsyncImage?") + @Test + fun `clicking swipe on the image invokes the expected callback`() { + val eventsRecorder = EventsRecorder(expectEvents = false) + ensureCalledOnce { callback -> + rule.setMediaViewerView( + aMediaViewerState( + downloadedMedia = AsyncData.Success( + LocalMedia(Uri.EMPTY, anImageInfo()) + ), + mediaInfo = anImageInfo(), + eventSink = eventsRecorder + ), + onBackPressed = callback, + ) + val imageContentDescription = rule.activity.getString(CommonStrings.common_image) + rule.onNodeWithContentDescription(imageContentDescription).performTouchInput { swipeDown() } + rule.mainClock.advanceTimeBy(1_000) + } + } + + @Test + fun `error case, click on retry emits the expected Event`() { + val eventsRecorder = EventsRecorder() + rule.setMediaViewerView( + aMediaViewerState( + downloadedMedia = AsyncData.Failure(IllegalStateException("error")), + mediaInfo = anImageInfo(), + eventSink = eventsRecorder + ), + ) + rule.clickOn(CommonStrings.action_retry) + eventsRecorder.assertSingle(MediaViewerEvents.RetryLoading) + } + + @Test + fun `error case, click on cancel emits the expected Event`() { + val eventsRecorder = EventsRecorder() + rule.setMediaViewerView( + aMediaViewerState( + downloadedMedia = AsyncData.Failure(IllegalStateException("error")), + mediaInfo = anImageInfo(), + eventSink = eventsRecorder + ), + ) + rule.clickOn(CommonStrings.action_cancel) + eventsRecorder.assertSingle(MediaViewerEvents.ClearLoadingError) + } +} + +private fun AndroidComposeTestRule.setMediaViewerView( + state: MediaViewerState, + onBackPressed: () -> Unit = EnsureNeverCalled(), +) { + setContent { + MediaViewerView( + state = state, + onBackPressed = onBackPressed, + ) + } +} From b843b5d9faf64f7c00c4ad1b83b06e29a72bac79 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 27 Feb 2024 09:53:14 +0100 Subject: [PATCH 2/9] Renaming function for code clarity. --- .../AttachmentsPreviewStateProvider.kt | 8 ++-- .../mediaviewer/api/local/MediaInfo.kt | 10 ++--- .../api/viewer/MediaViewerStateProvider.kt | 40 +++++++++---------- .../mediaviewer/MediaViewerPresenterTest.kt | 4 +- .../api/viewer/MediaViewerViewTest.kt | 18 ++++----- .../local/AndroidLocalMediaFactoryTest.kt | 4 +- .../mediaviewer/test/viewer/LocalMedia.kt | 4 +- 7 files changed, 44 insertions(+), 44 deletions(-) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewStateProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewStateProvider.kt index 1fdc52cf99..8dbf067b95 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewStateProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewStateProvider.kt @@ -21,21 +21,21 @@ import androidx.core.net.toUri import io.element.android.features.messages.impl.attachments.Attachment import io.element.android.libraries.mediaviewer.api.local.LocalMedia import io.element.android.libraries.mediaviewer.api.local.MediaInfo -import io.element.android.libraries.mediaviewer.api.local.aFileInfo -import io.element.android.libraries.mediaviewer.api.local.anImageInfo +import io.element.android.libraries.mediaviewer.api.local.anApkMediaInfo +import io.element.android.libraries.mediaviewer.api.local.anImageMediaInfo open class AttachmentsPreviewStateProvider : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( anAttachmentsPreviewState(), - anAttachmentsPreviewState(mediaInfo = aFileInfo()), + anAttachmentsPreviewState(mediaInfo = anApkMediaInfo()), anAttachmentsPreviewState(sendActionState = SendActionState.Sending.Uploading(0.5f)), anAttachmentsPreviewState(sendActionState = SendActionState.Failure(RuntimeException("error"))), ) } fun anAttachmentsPreviewState( - mediaInfo: MediaInfo = anImageInfo(), + mediaInfo: MediaInfo = anImageMediaInfo(), sendActionState: SendActionState = SendActionState.Idle ) = AttachmentsPreviewState( attachment = Attachment.Media( diff --git a/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/MediaInfo.kt b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/MediaInfo.kt index 1def7b4522..71dccfe4d4 100644 --- a/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/MediaInfo.kt +++ b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/local/MediaInfo.kt @@ -28,35 +28,35 @@ data class MediaInfo( val fileExtension: String, ) : Parcelable -fun anImageInfo(): MediaInfo = MediaInfo( +fun anImageMediaInfo(): MediaInfo = MediaInfo( "an image file.jpg", MimeTypes.Jpeg, "4MB", "jpg" ) -fun aVideoInfo(): MediaInfo = MediaInfo( +fun aVideoMediaInfo(): MediaInfo = MediaInfo( "a video file.mp4", MimeTypes.Mp4, "14MB", "mp4" ) -fun aPdfInfo(): MediaInfo = MediaInfo( +fun aPdfMediaInfo(): MediaInfo = MediaInfo( "a pdf file.pdf", MimeTypes.Pdf, "23MB", "pdf" ) -fun aFileInfo(): MediaInfo = MediaInfo( +fun anApkMediaInfo(): MediaInfo = MediaInfo( "an apk file.apk", MimeTypes.Apk, "50MB", "apk" ) -fun anAudioInfo(): MediaInfo = MediaInfo( +fun anAudioMediaInfo(): MediaInfo = MediaInfo( "an audio file.mp3", MimeTypes.Mp3, "7MB", diff --git a/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/viewer/MediaViewerStateProvider.kt b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/viewer/MediaViewerStateProvider.kt index d46fd64cc7..f7195f1c91 100644 --- a/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/viewer/MediaViewerStateProvider.kt +++ b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/viewer/MediaViewerStateProvider.kt @@ -21,11 +21,11 @@ import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.mediaviewer.api.local.LocalMedia import io.element.android.libraries.mediaviewer.api.local.MediaInfo -import io.element.android.libraries.mediaviewer.api.local.aFileInfo -import io.element.android.libraries.mediaviewer.api.local.aPdfInfo -import io.element.android.libraries.mediaviewer.api.local.aVideoInfo -import io.element.android.libraries.mediaviewer.api.local.anAudioInfo -import io.element.android.libraries.mediaviewer.api.local.anImageInfo +import io.element.android.libraries.mediaviewer.api.local.anApkMediaInfo +import io.element.android.libraries.mediaviewer.api.local.aPdfMediaInfo +import io.element.android.libraries.mediaviewer.api.local.aVideoMediaInfo +import io.element.android.libraries.mediaviewer.api.local.anAudioMediaInfo +import io.element.android.libraries.mediaviewer.api.local.anImageMediaInfo open class MediaViewerStateProvider : PreviewParameterProvider { override val values: Sequence @@ -35,47 +35,47 @@ open class MediaViewerStateProvider : PreviewParameterProvider aMediaViewerState(AsyncData.Failure(IllegalStateException("error"))), aMediaViewerState( AsyncData.Success( - LocalMedia(Uri.EMPTY, anImageInfo()) + LocalMedia(Uri.EMPTY, anImageMediaInfo()) ), - anImageInfo(), + anImageMediaInfo(), ), aMediaViewerState( AsyncData.Success( - LocalMedia(Uri.EMPTY, aVideoInfo()) + LocalMedia(Uri.EMPTY, aVideoMediaInfo()) ), - aVideoInfo(), + aVideoMediaInfo(), ), aMediaViewerState( AsyncData.Success( - LocalMedia(Uri.EMPTY, aPdfInfo()) + LocalMedia(Uri.EMPTY, aPdfMediaInfo()) ), - aPdfInfo(), + aPdfMediaInfo(), ), aMediaViewerState( AsyncData.Loading(), - aFileInfo(), + anApkMediaInfo(), ), aMediaViewerState( AsyncData.Success( - LocalMedia(Uri.EMPTY, aFileInfo()) + LocalMedia(Uri.EMPTY, anApkMediaInfo()) ), - aFileInfo(), + anApkMediaInfo(), ), aMediaViewerState( AsyncData.Loading(), - anAudioInfo(), + anAudioMediaInfo(), ), aMediaViewerState( AsyncData.Success( - LocalMedia(Uri.EMPTY, anAudioInfo()) + LocalMedia(Uri.EMPTY, anAudioMediaInfo()) ), - anAudioInfo(), + anAudioMediaInfo(), ), aMediaViewerState( AsyncData.Success( - LocalMedia(Uri.EMPTY, anImageInfo()) + LocalMedia(Uri.EMPTY, anImageMediaInfo()) ), - anImageInfo(), + anImageMediaInfo(), canDownload = false, canShare = false, ), @@ -84,7 +84,7 @@ open class MediaViewerStateProvider : PreviewParameterProvider fun aMediaViewerState( downloadedMedia: AsyncData = AsyncData.Uninitialized, - mediaInfo: MediaInfo = anImageInfo(), + mediaInfo: MediaInfo = anImageMediaInfo(), canDownload: Boolean = true, canShare: Boolean = true, eventSink: (MediaViewerEvents) -> Unit = {}, diff --git a/libraries/mediaviewer/api/src/test/kotlin/io/element/android/libraries/mediaviewer/MediaViewerPresenterTest.kt b/libraries/mediaviewer/api/src/test/kotlin/io/element/android/libraries/mediaviewer/MediaViewerPresenterTest.kt index c5b761411a..42a0a93c64 100644 --- a/libraries/mediaviewer/api/src/test/kotlin/io/element/android/libraries/mediaviewer/MediaViewerPresenterTest.kt +++ b/libraries/mediaviewer/api/src/test/kotlin/io/element/android/libraries/mediaviewer/MediaViewerPresenterTest.kt @@ -27,7 +27,7 @@ import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher import io.element.android.libraries.matrix.test.media.FakeMediaLoader import io.element.android.libraries.matrix.test.media.aMediaSource -import io.element.android.libraries.mediaviewer.api.local.aFileInfo +import io.element.android.libraries.mediaviewer.api.local.anApkMediaInfo import io.element.android.libraries.mediaviewer.api.viewer.MediaViewerEvents import io.element.android.libraries.mediaviewer.api.viewer.MediaViewerNode import io.element.android.libraries.mediaviewer.api.viewer.MediaViewerPresenter @@ -40,7 +40,7 @@ import kotlinx.coroutines.test.runTest import org.junit.Rule import org.junit.Test -private val TESTED_MEDIA_INFO = aFileInfo() +private val TESTED_MEDIA_INFO = anApkMediaInfo() class MediaViewerPresenterTest { @get:Rule diff --git a/libraries/mediaviewer/api/src/test/kotlin/io/element/android/libraries/mediaviewer/api/viewer/MediaViewerViewTest.kt b/libraries/mediaviewer/api/src/test/kotlin/io/element/android/libraries/mediaviewer/api/viewer/MediaViewerViewTest.kt index 1faeb0772e..e6c210a19d 100644 --- a/libraries/mediaviewer/api/src/test/kotlin/io/element/android/libraries/mediaviewer/api/viewer/MediaViewerViewTest.kt +++ b/libraries/mediaviewer/api/src/test/kotlin/io/element/android/libraries/mediaviewer/api/viewer/MediaViewerViewTest.kt @@ -28,7 +28,7 @@ import androidx.compose.ui.test.swipeDown import androidx.test.ext.junit.runners.AndroidJUnit4 import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.mediaviewer.api.local.LocalMedia -import io.element.android.libraries.mediaviewer.api.local.anImageInfo +import io.element.android.libraries.mediaviewer.api.local.anImageMediaInfo import io.element.android.libraries.ui.strings.CommonStrings import io.element.android.tests.testutils.EnsureNeverCalled import io.element.android.tests.testutils.EventsRecorder @@ -79,9 +79,9 @@ class MediaViewerViewTest { rule.setMediaViewerView( aMediaViewerState( downloadedMedia = AsyncData.Success( - LocalMedia(Uri.EMPTY, anImageInfo()) + LocalMedia(Uri.EMPTY, anImageMediaInfo()) ), - mediaInfo = anImageInfo(), + mediaInfo = anImageMediaInfo(), eventSink = eventsRecorder ), ) @@ -97,9 +97,9 @@ class MediaViewerViewTest { rule.setMediaViewerView( aMediaViewerState( downloadedMedia = AsyncData.Success( - LocalMedia(Uri.EMPTY, anImageInfo()) + LocalMedia(Uri.EMPTY, anImageMediaInfo()) ), - mediaInfo = anImageInfo(), + mediaInfo = anImageMediaInfo(), eventSink = eventsRecorder ), ) @@ -122,9 +122,9 @@ class MediaViewerViewTest { rule.setMediaViewerView( aMediaViewerState( downloadedMedia = AsyncData.Success( - LocalMedia(Uri.EMPTY, anImageInfo()) + LocalMedia(Uri.EMPTY, anImageMediaInfo()) ), - mediaInfo = anImageInfo(), + mediaInfo = anImageMediaInfo(), eventSink = eventsRecorder ), onBackPressed = callback, @@ -141,7 +141,7 @@ class MediaViewerViewTest { rule.setMediaViewerView( aMediaViewerState( downloadedMedia = AsyncData.Failure(IllegalStateException("error")), - mediaInfo = anImageInfo(), + mediaInfo = anImageMediaInfo(), eventSink = eventsRecorder ), ) @@ -155,7 +155,7 @@ class MediaViewerViewTest { rule.setMediaViewerView( aMediaViewerState( downloadedMedia = AsyncData.Failure(IllegalStateException("error")), - mediaInfo = anImageInfo(), + mediaInfo = anImageMediaInfo(), eventSink = eventsRecorder ), ) diff --git a/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/local/AndroidLocalMediaFactoryTest.kt b/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/local/AndroidLocalMediaFactoryTest.kt index 2563511ee2..d8f09c618f 100644 --- a/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/local/AndroidLocalMediaFactoryTest.kt +++ b/libraries/mediaviewer/impl/src/test/kotlin/io/element/android/libraries/mediaviewer/impl/local/AndroidLocalMediaFactoryTest.kt @@ -22,7 +22,7 @@ import io.element.android.libraries.core.mimetype.MimeTypes import io.element.android.libraries.matrix.api.media.MediaFile import io.element.android.libraries.matrix.test.media.FakeMediaFile import io.element.android.libraries.mediaviewer.api.local.MediaInfo -import io.element.android.libraries.mediaviewer.api.local.anImageInfo +import io.element.android.libraries.mediaviewer.api.local.anImageMediaInfo import io.element.android.libraries.mediaviewer.api.util.FileExtensionExtractorWithoutValidation import org.junit.Test import org.junit.runner.RunWith @@ -34,7 +34,7 @@ class AndroidLocalMediaFactoryTest { @Test fun `test AndroidLocalMediaFactory`() { val sut = createAndroidLocalMediaFactory() - val result = sut.createFromMediaFile(aMediaFile(), anImageInfo()) + val result = sut.createFromMediaFile(aMediaFile(), anImageMediaInfo()) assertThat(result.uri.toString()).endsWith("aPath") assertThat(result.info).isEqualTo( MediaInfo( diff --git a/libraries/mediaviewer/test/src/main/kotlin/io/element/android/libraries/mediaviewer/test/viewer/LocalMedia.kt b/libraries/mediaviewer/test/src/main/kotlin/io/element/android/libraries/mediaviewer/test/viewer/LocalMedia.kt index 876e82000d..f9102c0ee2 100644 --- a/libraries/mediaviewer/test/src/main/kotlin/io/element/android/libraries/mediaviewer/test/viewer/LocalMedia.kt +++ b/libraries/mediaviewer/test/src/main/kotlin/io/element/android/libraries/mediaviewer/test/viewer/LocalMedia.kt @@ -19,11 +19,11 @@ package io.element.android.libraries.mediaviewer.test.viewer import android.net.Uri import io.element.android.libraries.mediaviewer.api.local.LocalMedia import io.element.android.libraries.mediaviewer.api.local.MediaInfo -import io.element.android.libraries.mediaviewer.api.local.anImageInfo +import io.element.android.libraries.mediaviewer.api.local.anImageMediaInfo fun aLocalMedia( uri: Uri, - mediaInfo: MediaInfo = anImageInfo(), + mediaInfo: MediaInfo = anImageMediaInfo(), ) = LocalMedia( uri = uri, info = mediaInfo From 2f234573e907e0584cc22ff2c178683d6af7206d Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 27 Feb 2024 10:13:38 +0100 Subject: [PATCH 3/9] Add test on TextField for `TextFieldValue` parameter --- .../theme/components/TextField.kt | 40 ++++++++++++++++++- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/TextField.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/TextField.kt index e5f0434c50..f2c004d2ca 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/TextField.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/components/TextField.kt @@ -39,6 +39,7 @@ import androidx.compose.ui.layout.boundsInWindow import androidx.compose.ui.layout.onGloballyPositioned import androidx.compose.ui.platform.LocalAutofill import androidx.compose.ui.platform.LocalAutofillTree +import androidx.compose.ui.text.TextRange import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.input.TextFieldValue import androidx.compose.ui.text.input.VisualTransformation @@ -116,7 +117,7 @@ fun TextField( keyboardOptions: KeyboardOptions = KeyboardOptions.Default, keyboardActions: KeyboardActions = KeyboardActions.Default, singleLine: Boolean = false, - maxLines: Int = Int.MAX_VALUE, + maxLines: Int = if (singleLine) 1 else Int.MAX_VALUE, interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }, shape: Shape = TextFieldDefaults.shape, colors: TextFieldColors = TextFieldDefaults.colors() @@ -163,9 +164,44 @@ private fun ContentToPreview() { allBooleans.forEach { enabled -> allBooleans.forEach { readonly -> TextField( + value = "Hello er=${isError.asInt()}, en=${enabled.asInt()}, ro=${readonly.asInt()}", + onValueChange = {}, + label = { Text(text = "label") }, + isError = isError, + enabled = enabled, + readOnly = readonly, + ) + Spacer(modifier = Modifier.height(2.dp)) + } + } + } + } +} + +@Preview(group = PreviewGroup.TextFields) +@Composable +internal fun TextFieldValueLightPreview() = + ElementPreviewLight { TextFieldValueContentToPreview() } + +@Preview(group = PreviewGroup.TextFields) +@Composable +internal fun TextFieldValueTextFieldDarkPreview() = + ElementPreviewDark { TextFieldValueContentToPreview() } + +@ExcludeFromCoverage +@Composable +private fun TextFieldValueContentToPreview() { + Column(modifier = Modifier.padding(4.dp)) { + allBooleans.forEach { isError -> + allBooleans.forEach { enabled -> + allBooleans.forEach { readonly -> + TextField( + value = TextFieldValue( + text = "Hello er=${isError.asInt()}, en=${enabled.asInt()}, ro=${readonly.asInt()}", + selection = TextRange(0, "Hello".length), + ), onValueChange = {}, label = { Text(text = "label") }, - value = "Hello er=${isError.asInt()}, en=${enabled.asInt()}, ro=${readonly.asInt()}", isError = isError, enabled = enabled, readOnly = readonly, From ca35c0a800b3067f75ba4cfbb372ed497566be50 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 27 Feb 2024 10:41:14 +0100 Subject: [PATCH 4/9] Remove useless log. --- .../libraries/sessionstorage/impl/DatabaseSessionStore.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/libraries/session-storage/impl/src/main/kotlin/io/element/android/libraries/sessionstorage/impl/DatabaseSessionStore.kt b/libraries/session-storage/impl/src/main/kotlin/io/element/android/libraries/sessionstorage/impl/DatabaseSessionStore.kt index 5ccf62c61d..3d3b262121 100644 --- a/libraries/session-storage/impl/src/main/kotlin/io/element/android/libraries/sessionstorage/impl/DatabaseSessionStore.kt +++ b/libraries/session-storage/impl/src/main/kotlin/io/element/android/libraries/sessionstorage/impl/DatabaseSessionStore.kt @@ -103,7 +103,6 @@ class DatabaseSessionStore @Inject constructor( } override fun sessionsFlow(): Flow> { - Timber.w("Observing session list!") return database.sessionDataQueries.selectAll() .asFlow() .mapToList(dispatchers.io) From 0c6a26f6c17bf020e447d27b4a33869966a5074d Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 27 Feb 2024 11:39:17 +0100 Subject: [PATCH 5/9] Add test on DefaultSessionObserver. --- .../session-storage/impl/build.gradle.kts | 1 + .../impl/DatabaseSessionStoreTests.kt | 15 +-- .../libraries/sessionstorage/impl/Fixtures.kt | 34 +++++++ .../observer/DefaultSessionObserverTest.kt | 93 +++++++++++++++++++ .../impl/observer/TestSessionListener.kt | 41 ++++++++ 5 files changed, 170 insertions(+), 14 deletions(-) create mode 100644 libraries/session-storage/impl/src/test/kotlin/io/element/android/libraries/sessionstorage/impl/Fixtures.kt create mode 100644 libraries/session-storage/impl/src/test/kotlin/io/element/android/libraries/sessionstorage/impl/observer/DefaultSessionObserverTest.kt create mode 100644 libraries/session-storage/impl/src/test/kotlin/io/element/android/libraries/sessionstorage/impl/observer/TestSessionListener.kt diff --git a/libraries/session-storage/impl/build.gradle.kts b/libraries/session-storage/impl/build.gradle.kts index cfbfa4c57d..a083d56480 100644 --- a/libraries/session-storage/impl/build.gradle.kts +++ b/libraries/session-storage/impl/build.gradle.kts @@ -45,6 +45,7 @@ dependencies { testImplementation(libs.test.turbine) testImplementation(libs.coroutines.test) testImplementation(libs.sqldelight.driver.jvm) + testImplementation(projects.tests.testutils) } sqldelight { diff --git a/libraries/session-storage/impl/src/test/kotlin/io/element/android/libraries/sessionstorage/impl/DatabaseSessionStoreTests.kt b/libraries/session-storage/impl/src/test/kotlin/io/element/android/libraries/sessionstorage/impl/DatabaseSessionStoreTests.kt index 760eefd20c..09d0ef867b 100644 --- a/libraries/session-storage/impl/src/test/kotlin/io/element/android/libraries/sessionstorage/impl/DatabaseSessionStoreTests.kt +++ b/libraries/session-storage/impl/src/test/kotlin/io/element/android/libraries/sessionstorage/impl/DatabaseSessionStoreTests.kt @@ -22,7 +22,6 @@ import com.google.common.truth.Truth.assertThat import io.element.android.libraries.core.coroutine.CoroutineDispatchers import io.element.android.libraries.matrix.session.SessionData import io.element.android.libraries.sessionstorage.api.LoggedInState -import io.element.android.libraries.sessionstorage.api.LoginType import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.UnconfinedTestDispatcher import kotlinx.coroutines.test.runTest @@ -33,19 +32,7 @@ class DatabaseSessionStoreTests { private lateinit var database: SessionDatabase private lateinit var databaseSessionStore: DatabaseSessionStore - private val aSessionData = SessionData( - userId = "userId", - deviceId = "deviceId", - accessToken = "accessToken", - refreshToken = "refreshToken", - homeserverUrl = "homeserverUrl", - slidingSyncProxy = null, - loginTimestamp = null, - oidcData = "aOidcData", - isTokenValid = 1, - loginType = LoginType.UNKNOWN.name, - passphrase = null, - ) + private val aSessionData = aSessionData() @OptIn(ExperimentalCoroutinesApi::class) @Before diff --git a/libraries/session-storage/impl/src/test/kotlin/io/element/android/libraries/sessionstorage/impl/Fixtures.kt b/libraries/session-storage/impl/src/test/kotlin/io/element/android/libraries/sessionstorage/impl/Fixtures.kt new file mode 100644 index 0000000000..341e5e0e92 --- /dev/null +++ b/libraries/session-storage/impl/src/test/kotlin/io/element/android/libraries/sessionstorage/impl/Fixtures.kt @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2024 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.sessionstorage.impl + +import io.element.android.libraries.matrix.session.SessionData +import io.element.android.libraries.sessionstorage.api.LoginType + +internal fun aSessionData() = SessionData( + userId = "userId", + deviceId = "deviceId", + accessToken = "accessToken", + refreshToken = "refreshToken", + homeserverUrl = "homeserverUrl", + slidingSyncProxy = null, + loginTimestamp = null, + oidcData = "aOidcData", + isTokenValid = 1, + loginType = LoginType.UNKNOWN.name, + passphrase = null, +) diff --git a/libraries/session-storage/impl/src/test/kotlin/io/element/android/libraries/sessionstorage/impl/observer/DefaultSessionObserverTest.kt b/libraries/session-storage/impl/src/test/kotlin/io/element/android/libraries/sessionstorage/impl/observer/DefaultSessionObserverTest.kt new file mode 100644 index 0000000000..c0fc805fc6 --- /dev/null +++ b/libraries/session-storage/impl/src/test/kotlin/io/element/android/libraries/sessionstorage/impl/observer/DefaultSessionObserverTest.kt @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2024 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.sessionstorage.impl.observer + +import app.cash.sqldelight.driver.jdbc.sqlite.JdbcSqliteDriver +import io.element.android.libraries.core.coroutine.CoroutineDispatchers +import io.element.android.libraries.sessionstorage.impl.DatabaseSessionStore +import io.element.android.libraries.sessionstorage.impl.SessionDatabase +import io.element.android.libraries.sessionstorage.impl.aSessionData +import io.element.android.libraries.sessionstorage.impl.toApiModel +import io.element.android.tests.testutils.testCoroutineDispatchers +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.cancelChildren +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.UnconfinedTestDispatcher +import kotlinx.coroutines.test.runCurrent +import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.junit.Test + +@OptIn(ExperimentalCoroutinesApi::class) class DefaultSessionObserverTest { + private lateinit var database: SessionDatabase + private lateinit var databaseSessionStore: DatabaseSessionStore + + @OptIn(ExperimentalCoroutinesApi::class) + @Before + fun setup() { + // Initialise in memory SQLite driver + val driver = JdbcSqliteDriver(JdbcSqliteDriver.IN_MEMORY) + SessionDatabase.Schema.create(driver) + database = SessionDatabase(driver) + databaseSessionStore = DatabaseSessionStore( + database = database, + dispatchers = CoroutineDispatchers( + io = UnconfinedTestDispatcher(), + computation = UnconfinedTestDispatcher(), + main = UnconfinedTestDispatcher(), + ) + ) + } + + @Test + fun `adding data invokes onSessionCreated`() = runTest { + val sessionData = aSessionData() + val sut = createDefaultSessionObserver() + runCurrent() + val listener = TestSessionListener() + sut.addListener(listener) + databaseSessionStore.storeData(sessionData.toApiModel()) + listener.assertEvents(TestSessionListener.Event.Created(sessionData.userId)) + sut.removeListener(listener) + coroutineContext.cancelChildren() + } + + @Test + fun `adding and deleting data invokes onSessionCreated and onSessionDeleted`() = runTest { + val sessionData = aSessionData() + val sut = createDefaultSessionObserver() + runCurrent() + val listener = TestSessionListener() + sut.addListener(listener) + databaseSessionStore.storeData(sessionData.toApiModel()) + listener.assertEvents(TestSessionListener.Event.Created(sessionData.userId)) + databaseSessionStore.removeSession(sessionData.userId) + listener.assertEvents( + TestSessionListener.Event.Created(sessionData.userId), + TestSessionListener.Event.Deleted(sessionData.userId), + ) + coroutineContext.cancelChildren() + } + + private fun TestScope.createDefaultSessionObserver(): DefaultSessionObserver { + return DefaultSessionObserver( + sessionStore = databaseSessionStore, + coroutineScope = this, + dispatchers = testCoroutineDispatchers(useUnconfinedTestDispatcher = true), + ) + } +} diff --git a/libraries/session-storage/impl/src/test/kotlin/io/element/android/libraries/sessionstorage/impl/observer/TestSessionListener.kt b/libraries/session-storage/impl/src/test/kotlin/io/element/android/libraries/sessionstorage/impl/observer/TestSessionListener.kt new file mode 100644 index 0000000000..2ec826ec4a --- /dev/null +++ b/libraries/session-storage/impl/src/test/kotlin/io/element/android/libraries/sessionstorage/impl/observer/TestSessionListener.kt @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2024 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.sessionstorage.impl.observer + +import com.google.common.truth.Truth.assertThat +import io.element.android.libraries.sessionstorage.api.observer.SessionListener + +class TestSessionListener : SessionListener { + sealed class Event { + data class Created(val userId: String) : Event() + data class Deleted(val userId: String) : Event() + } + + private val trackRecord: MutableList = mutableListOf() + + override suspend fun onSessionCreated(userId: String) { + trackRecord.add(Event.Created(userId)) + } + + override suspend fun onSessionDeleted(userId: String) { + trackRecord.add(Event.Deleted(userId)) + } + + fun assertEvents(vararg events: Event) { + assertThat(trackRecord).containsExactly(*events) + } +} From 0c8631417a1f13b333a8247397ab057cf43904b4 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 27 Feb 2024 12:04:59 +0100 Subject: [PATCH 6/9] Add test for DatabaseSessionStore.getAllSessions(), and update data when session is not found. --- .../impl/DatabaseSessionStoreTests.kt | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/libraries/session-storage/impl/src/test/kotlin/io/element/android/libraries/sessionstorage/impl/DatabaseSessionStoreTests.kt b/libraries/session-storage/impl/src/test/kotlin/io/element/android/libraries/sessionstorage/impl/DatabaseSessionStoreTests.kt index 09d0ef867b..46e90f6d52 100644 --- a/libraries/session-storage/impl/src/test/kotlin/io/element/android/libraries/sessionstorage/impl/DatabaseSessionStoreTests.kt +++ b/libraries/session-storage/impl/src/test/kotlin/io/element/android/libraries/sessionstorage/impl/DatabaseSessionStoreTests.kt @@ -83,6 +83,24 @@ class DatabaseSessionStoreTests { assertThat(latestSession).isEqualTo(aSessionData) } + @Test + fun `getAllSessions should return all the sessions`() = runTest { + val noSessions = databaseSessionStore.getAllSessions() + assertThat(noSessions).isEmpty() + database.sessionDataQueries.insertSessionData(aSessionData) + val otherSessionData = aSessionData.copy(userId = "otherUserId") + database.sessionDataQueries.insertSessionData(otherSessionData) + val allSessions = databaseSessionStore.getAllSessions().map { + it.toDbModel() + } + assertThat(allSessions).isEqualTo( + listOf( + aSessionData, + otherSessionData, + ) + ) + } + @Test fun `getSession returns a matching session in DB if exists`() = runTest { database.sessionDataQueries.insertSessionData(aSessionData) @@ -160,4 +178,51 @@ class DatabaseSessionStoreTests { assertThat(alteredSession.oidcData).isEqualTo(secondSessionData.oidcData) assertThat(alteredSession.passphrase).isEqualTo(secondSessionData.passphrase) } + + @Test + fun `update data, session not found`() = runTest { + val firstSessionData = SessionData( + userId = "userId", + deviceId = "deviceId", + accessToken = "accessToken", + refreshToken = "refreshToken", + homeserverUrl = "homeserverUrl", + slidingSyncProxy = "slidingSyncProxy", + loginTimestamp = 1, + oidcData = "aOidcData", + isTokenValid = 1, + loginType = null, + passphrase = "aPassphrase", + ) + val secondSessionData = SessionData( + userId = "userIdUnknown", + deviceId = "deviceIdAltered", + accessToken = "accessTokenAltered", + refreshToken = "refreshTokenAltered", + homeserverUrl = "homeserverUrlAltered", + slidingSyncProxy = "slidingSyncProxyAltered", + loginTimestamp = 2, + oidcData = "aOidcDataAltered", + isTokenValid = 1, + loginType = null, + passphrase = "aPassphraseAltered", + ) + assertThat(firstSessionData.userId).isNotEqualTo(secondSessionData.userId) + + database.sessionDataQueries.insertSessionData(firstSessionData) + databaseSessionStore.updateData(secondSessionData.toApiModel()) + + // Get the session and check that it has not been altered + val notAlteredSession = databaseSessionStore.getSession(firstSessionData.userId)!!.toDbModel() + + assertThat(notAlteredSession.userId).isEqualTo(firstSessionData.userId) + assertThat(notAlteredSession.deviceId).isEqualTo(firstSessionData.deviceId) + assertThat(notAlteredSession.accessToken).isEqualTo(firstSessionData.accessToken) + assertThat(notAlteredSession.refreshToken).isEqualTo(firstSessionData.refreshToken) + assertThat(notAlteredSession.homeserverUrl).isEqualTo(firstSessionData.homeserverUrl) + assertThat(notAlteredSession.slidingSyncProxy).isEqualTo(firstSessionData.slidingSyncProxy) + assertThat(notAlteredSession.loginTimestamp).isEqualTo(firstSessionData.loginTimestamp) + assertThat(notAlteredSession.oidcData).isEqualTo(firstSessionData.oidcData) + assertThat(notAlteredSession.passphrase).isEqualTo(firstSessionData.passphrase) + } } From 63787eab4fbd1088ce6deff49897d0669fcb0983 Mon Sep 17 00:00:00 2001 From: ElementBot Date: Tue, 27 Feb 2024 11:21:37 +0000 Subject: [PATCH 7/9] Update screenshots --- ...l_TextFields_TextFieldValueLight_0_null,NEXUS_5,1.0,en].png | 3 +++ ...elds_TextFieldValueTextFieldDark_0_null,NEXUS_5,1.0,en].png | 3 +++ 2 files changed, 6 insertions(+) create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_TextFieldValueLight_null_TextFields_TextFieldValueLight_0_null,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_TextFieldValueTextFieldDark_null_TextFields_TextFieldValueTextFieldDark_0_null,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_TextFieldValueLight_null_TextFields_TextFieldValueLight_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_TextFieldValueLight_null_TextFields_TextFieldValueLight_0_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..43cbebf3ee --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_TextFieldValueLight_null_TextFields_TextFieldValueLight_0_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:98631172faca52549bdbc5890e06dcc9b25b70111c21e6a1d2aaf7412fbe52b4 +size 38596 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_TextFieldValueTextFieldDark_null_TextFields_TextFieldValueTextFieldDark_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_TextFieldValueTextFieldDark_null_TextFields_TextFieldValueTextFieldDark_0_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..80026ead6c --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.designsystem.theme.components_TextFieldValueTextFieldDark_null_TextFields_TextFieldValueTextFieldDark_0_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7407a2ad190a77b2fc30c1af55cdf33a0087f1104bd9a7fbfdd230b67ce0c535 +size 38147 From 3e65f4bc99a3c6c5d7fec2a30932c941371e6f51 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 27 Feb 2024 14:40:50 +0100 Subject: [PATCH 8/9] Fix import order. --- .../mediaviewer/api/viewer/MediaViewerStateProvider.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/viewer/MediaViewerStateProvider.kt b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/viewer/MediaViewerStateProvider.kt index f7195f1c91..4b0719ed3a 100644 --- a/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/viewer/MediaViewerStateProvider.kt +++ b/libraries/mediaviewer/api/src/main/kotlin/io/element/android/libraries/mediaviewer/api/viewer/MediaViewerStateProvider.kt @@ -21,9 +21,9 @@ import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.libraries.architecture.AsyncData import io.element.android.libraries.mediaviewer.api.local.LocalMedia import io.element.android.libraries.mediaviewer.api.local.MediaInfo -import io.element.android.libraries.mediaviewer.api.local.anApkMediaInfo import io.element.android.libraries.mediaviewer.api.local.aPdfMediaInfo import io.element.android.libraries.mediaviewer.api.local.aVideoMediaInfo +import io.element.android.libraries.mediaviewer.api.local.anApkMediaInfo import io.element.android.libraries.mediaviewer.api.local.anAudioMediaInfo import io.element.android.libraries.mediaviewer.api.local.anImageMediaInfo From 142338be0753d4e3ef6e23706febe492d3ee71bb Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 27 Feb 2024 14:42:43 +0100 Subject: [PATCH 9/9] An interface is enough. --- .../sessionstorage/impl/observer/TestSessionListener.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libraries/session-storage/impl/src/test/kotlin/io/element/android/libraries/sessionstorage/impl/observer/TestSessionListener.kt b/libraries/session-storage/impl/src/test/kotlin/io/element/android/libraries/sessionstorage/impl/observer/TestSessionListener.kt index 2ec826ec4a..5317f2df0b 100644 --- a/libraries/session-storage/impl/src/test/kotlin/io/element/android/libraries/sessionstorage/impl/observer/TestSessionListener.kt +++ b/libraries/session-storage/impl/src/test/kotlin/io/element/android/libraries/sessionstorage/impl/observer/TestSessionListener.kt @@ -20,9 +20,9 @@ import com.google.common.truth.Truth.assertThat import io.element.android.libraries.sessionstorage.api.observer.SessionListener class TestSessionListener : SessionListener { - sealed class Event { - data class Created(val userId: String) : Event() - data class Deleted(val userId: String) : Event() + sealed interface Event { + data class Created(val userId: String) : Event + data class Deleted(val userId: String) : Event } private val trackRecord: MutableList = mutableListOf()