ganfra
1 year ago
15 changed files with 354 additions and 53 deletions
@ -0,0 +1,95 @@
@@ -0,0 +1,95 @@
|
||||
/* |
||||
* Copyright (c) 2023 New Vector Ltd |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0 |
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package io.element.android.features.messages.attachments |
||||
|
||||
import androidx.media3.common.MimeTypes |
||||
import app.cash.molecule.RecompositionClock |
||||
import app.cash.molecule.moleculeFlow |
||||
import app.cash.turbine.test |
||||
import com.google.common.truth.Truth.assertThat |
||||
import io.element.android.features.messages.fixtures.aLocalMedia |
||||
import io.element.android.features.messages.impl.attachments.Attachment |
||||
import io.element.android.features.messages.impl.attachments.preview.AttachmentsPreviewEvents |
||||
import io.element.android.features.messages.impl.attachments.preview.AttachmentsPreviewPresenter |
||||
import io.element.android.features.messages.impl.media.local.LocalMedia |
||||
import io.element.android.libraries.architecture.Async |
||||
import io.element.android.libraries.matrix.api.room.MatrixRoom |
||||
import io.element.android.libraries.matrix.test.FAKE_DELAY_IN_MS |
||||
import io.element.android.libraries.matrix.test.room.FakeMatrixRoom |
||||
import io.element.android.libraries.mediaupload.api.MediaPreProcessor |
||||
import io.element.android.libraries.mediaupload.api.MediaSender |
||||
import io.element.android.libraries.mediaupload.test.FakeMediaPreProcessor |
||||
import kotlinx.coroutines.test.runTest |
||||
import org.junit.Test |
||||
|
||||
class AttachmentsPreviewPresenterTest { |
||||
|
||||
private val mediaPreProcessor = FakeMediaPreProcessor() |
||||
|
||||
@Test |
||||
fun `present - send media success scenario`() = runTest { |
||||
val room = FakeMatrixRoom() |
||||
val presenter = anAttachmentsPreviewPresenter(room = room) |
||||
moleculeFlow(RecompositionClock.Immediate) { |
||||
presenter.present() |
||||
}.test { |
||||
val initialState = awaitItem() |
||||
assertThat(initialState.sendActionState).isEqualTo(Async.Uninitialized) |
||||
initialState.eventSink(AttachmentsPreviewEvents.SendAttachment) |
||||
val loadingState = awaitItem() |
||||
assertThat(loadingState.sendActionState).isEqualTo(Async.Loading<Unit>()) |
||||
testScheduler.advanceTimeBy(FAKE_DELAY_IN_MS) |
||||
val successState = awaitItem() |
||||
assertThat(successState.sendActionState).isEqualTo(Async.Success(Unit)) |
||||
assertThat(room.sendMediaCount).isEqualTo(1) |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
fun `present - send media failure scenario`() = runTest { |
||||
val room = FakeMatrixRoom() |
||||
val failure = MediaPreProcessor.Failure(null) |
||||
room.givenSendMediaResult(Result.failure(failure)) |
||||
val presenter = anAttachmentsPreviewPresenter(room = room) |
||||
moleculeFlow(RecompositionClock.Immediate) { |
||||
presenter.present() |
||||
}.test { |
||||
val initialState = awaitItem() |
||||
assertThat(initialState.sendActionState).isEqualTo(Async.Uninitialized) |
||||
initialState.eventSink(AttachmentsPreviewEvents.SendAttachment) |
||||
val loadingState = awaitItem() |
||||
assertThat(loadingState.sendActionState).isEqualTo(Async.Loading<Unit>()) |
||||
testScheduler.advanceTimeBy(FAKE_DELAY_IN_MS) |
||||
val failureState = awaitItem() |
||||
assertThat(failureState.sendActionState).isEqualTo(Async.Failure<Unit>(failure)) |
||||
assertThat(room.sendMediaCount).isEqualTo(0) |
||||
failureState.eventSink(AttachmentsPreviewEvents.ClearSendState) |
||||
val clearedState = awaitItem() |
||||
assertThat(clearedState.sendActionState).isEqualTo(Async.Uninitialized) |
||||
} |
||||
} |
||||
|
||||
private fun anAttachmentsPreviewPresenter( |
||||
localMedia: LocalMedia = aLocalMedia(mimeType = MimeTypes.IMAGE_JPEG), |
||||
room: MatrixRoom = FakeMatrixRoom() |
||||
): AttachmentsPreviewPresenter { |
||||
return AttachmentsPreviewPresenter( |
||||
attachment = Attachment.Media(localMedia, compressIfPossible = false), |
||||
mediaSender = MediaSender(mediaPreProcessor, room) |
||||
) |
||||
} |
||||
} |
@ -0,0 +1,36 @@
@@ -0,0 +1,36 @@
|
||||
/* |
||||
* Copyright (c) 2023 New Vector Ltd |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0 |
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package io.element.android.features.messages.fixtures |
||||
|
||||
import android.net.Uri |
||||
import io.element.android.features.messages.impl.attachments.Attachment |
||||
import io.element.android.features.messages.impl.media.local.LocalMedia |
||||
import io.mockk.mockk |
||||
|
||||
fun aLocalMedia( |
||||
uri: Uri = mockk("localMediaUri"), |
||||
mimeType: String |
||||
) = LocalMedia( |
||||
uri = uri, |
||||
mimeType = mimeType |
||||
) |
||||
|
||||
fun aMediaAttachment(localMedia: LocalMedia, compressIfPossible: Boolean = true) = Attachment.Media( |
||||
localMedia = localMedia, |
||||
compressIfPossible = compressIfPossible, |
||||
) |
||||
|
@ -0,0 +1,37 @@
@@ -0,0 +1,37 @@
|
||||
/* |
||||
* Copyright (c) 2023 New Vector Ltd |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0 |
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package io.element.android.features.messages.media |
||||
|
||||
import android.net.Uri |
||||
import io.element.android.features.messages.fixtures.aLocalMedia |
||||
import io.element.android.features.messages.impl.media.local.LocalMedia |
||||
import io.element.android.features.messages.impl.media.local.LocalMediaFactory |
||||
import io.element.android.libraries.core.mimetype.MimeTypes |
||||
import io.element.android.libraries.matrix.api.media.MediaFile |
||||
|
||||
class FakeLocalMediaFactory() : LocalMediaFactory { |
||||
|
||||
var fallbackMimeType: String = MimeTypes.OctetStream |
||||
|
||||
override fun createFromMediaFile(mediaFile: MediaFile, mimeType: String?): LocalMedia { |
||||
return aLocalMedia(mimeType = mimeType ?: fallbackMimeType) |
||||
} |
||||
|
||||
override fun createFromUri(uri: Uri, mimeType: String?): LocalMedia { |
||||
return aLocalMedia(uri, mimeType ?: fallbackMimeType) |
||||
} |
||||
} |
@ -0,0 +1,102 @@
@@ -0,0 +1,102 @@
|
||||
/* |
||||
* Copyright (c) 2023 New Vector Ltd |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0 |
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package io.element.android.features.messages.media.viewer |
||||
|
||||
import androidx.media3.common.MimeTypes |
||||
import app.cash.molecule.RecompositionClock |
||||
import app.cash.molecule.moleculeFlow |
||||
import app.cash.turbine.test |
||||
import com.google.common.truth.Truth.assertThat |
||||
import io.element.android.features.messages.impl.media.viewer.MediaViewerEvents |
||||
import io.element.android.features.messages.impl.media.viewer.MediaViewerNode |
||||
import io.element.android.features.messages.impl.media.viewer.MediaViewerPresenter |
||||
import io.element.android.features.messages.media.FakeLocalMediaFactory |
||||
import io.element.android.libraries.architecture.Async |
||||
import io.element.android.libraries.matrix.test.FAKE_DELAY_IN_MS |
||||
import io.element.android.libraries.matrix.test.media.FakeMediaLoader |
||||
import io.element.android.libraries.matrix.test.media.aMediaSource |
||||
import kotlinx.coroutines.test.runTest |
||||
import org.junit.Test |
||||
|
||||
private const val TESTED_MIME_TYPE = MimeTypes.IMAGE_JPEG |
||||
private const val TESTED_MEDIA_NAME = "MediaName" |
||||
|
||||
class MediaViewerPresenterTest { |
||||
|
||||
private val localMediaFactory = FakeLocalMediaFactory() |
||||
private val mediaLoader = FakeMediaLoader() |
||||
|
||||
@Test |
||||
fun `present - download media success scenario`() = runTest { |
||||
val presenter = aMediaViewerPresenter() |
||||
moleculeFlow(RecompositionClock.Immediate) { |
||||
presenter.present() |
||||
}.test { |
||||
val initialState = awaitItem() |
||||
assertThat(initialState.downloadedMedia).isEqualTo(Async.Uninitialized) |
||||
assertThat(initialState.name).isEqualTo(TESTED_MEDIA_NAME) |
||||
val loadingState = awaitItem() |
||||
assertThat(loadingState.downloadedMedia).isInstanceOf(Async.Loading::class.java) |
||||
testScheduler.advanceTimeBy(FAKE_DELAY_IN_MS) |
||||
val successState = awaitItem() |
||||
val successData = successState.downloadedMedia.dataOrNull() |
||||
assertThat(successState.downloadedMedia).isInstanceOf(Async.Success::class.java) |
||||
assertThat(successData).isNotNull() |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
fun `present - download media failure then retry with success scenario`() = runTest { |
||||
val presenter = aMediaViewerPresenter() |
||||
moleculeFlow(RecompositionClock.Immediate) { |
||||
presenter.present() |
||||
}.test { |
||||
mediaLoader.shouldFail = true |
||||
val initialState = awaitItem() |
||||
assertThat(initialState.downloadedMedia).isEqualTo(Async.Uninitialized) |
||||
assertThat(initialState.name).isEqualTo(TESTED_MEDIA_NAME) |
||||
val loadingState = awaitItem() |
||||
assertThat(loadingState.downloadedMedia).isInstanceOf(Async.Loading::class.java) |
||||
testScheduler.advanceTimeBy(FAKE_DELAY_IN_MS) |
||||
val failureState = awaitItem() |
||||
assertThat(failureState.downloadedMedia).isInstanceOf(Async.Failure::class.java) |
||||
mediaLoader.shouldFail = false |
||||
failureState.eventSink(MediaViewerEvents.RetryLoading) |
||||
//There is one recomposition because of the retry mechanism |
||||
skipItems(1) |
||||
val retryLoadingState = awaitItem() |
||||
assertThat(retryLoadingState.downloadedMedia).isInstanceOf(Async.Loading::class.java) |
||||
testScheduler.advanceTimeBy(FAKE_DELAY_IN_MS) |
||||
val successState = awaitItem() |
||||
val successData = successState.downloadedMedia.dataOrNull() |
||||
assertThat(successState.downloadedMedia).isInstanceOf(Async.Success::class.java) |
||||
assertThat(successData).isNotNull() |
||||
} |
||||
} |
||||
|
||||
private fun aMediaViewerPresenter(mimeType: String = TESTED_MIME_TYPE): MediaViewerPresenter { |
||||
return MediaViewerPresenter( |
||||
inputs = MediaViewerNode.Inputs( |
||||
name = TESTED_MEDIA_NAME, |
||||
mediaSource = aMediaSource(), |
||||
mimeType = mimeType |
||||
), |
||||
localMediaFactory = localMediaFactory, |
||||
mediaLoader = mediaLoader |
||||
) |
||||
} |
||||
} |
Loading…
Reference in new issue