diff --git a/Samples/Flix/build.gradle b/Samples/Flix/build.gradle index 9c049a5d8..4c87606d1 100644 --- a/Samples/Flix/build.gradle +++ b/Samples/Flix/build.gradle @@ -129,6 +129,7 @@ dependencies { androidTestImplementation 'androidx.test.espresso:espresso-core:3.7.0' androidTestImplementation "androidx.test:rules:1.7.0" androidTestImplementation "androidx.compose.ui:ui-test-junit4:${versions.compose.ui}" + androidTestImplementation "androidx.test:core-ktx:1.4.0" } testify { diff --git a/Samples/Flix/src/androidTest/assets/screenshots/37-1080x2220@440dp-en_US/HomeScreenTest_homeScreenBottomSheet.png b/Samples/Flix/src/androidTest/assets/screenshots/37-1080x2220@440dp-en_US/HomeScreenTest_homeScreenBottomSheet.png new file mode 100644 index 000000000..66290159d Binary files /dev/null and b/Samples/Flix/src/androidTest/assets/screenshots/37-1080x2220@440dp-en_US/HomeScreenTest_homeScreenBottomSheet.png differ diff --git a/Samples/Flix/src/androidTest/assets/screenshots/37-1080x2220@440dp-fa/HomeScreenTest_homeScreenBottomSheet_RTL.png b/Samples/Flix/src/androidTest/assets/screenshots/37-1080x2220@440dp-fa/HomeScreenTest_homeScreenBottomSheet_RTL.png new file mode 100644 index 000000000..aaa83de9f Binary files /dev/null and b/Samples/Flix/src/androidTest/assets/screenshots/37-1080x2220@440dp-fa/HomeScreenTest_homeScreenBottomSheet_RTL.png differ diff --git a/Samples/Flix/src/androidTest/java/dev/testify/samples/flix/ui/homescreen/HomeScreenTest.kt b/Samples/Flix/src/androidTest/java/dev/testify/samples/flix/ui/homescreen/HomeScreenTest.kt new file mode 100644 index 000000000..a32051b7b --- /dev/null +++ b/Samples/Flix/src/androidTest/java/dev/testify/samples/flix/ui/homescreen/HomeScreenTest.kt @@ -0,0 +1,107 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2026 ndtp + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package dev.testify.samples.flix.ui.homescreen + +import androidx.compose.runtime.Composable +import androidx.compose.ui.res.stringResource +import androidx.test.core.app.launchActivity +import androidx.test.platform.app.InstrumentationRegistry +import coil.Coil +import coil.ImageLoader +import dev.testify.ComposableTestActivity +import dev.testify.annotation.ScreenshotInstrumentation +import dev.testify.compose.scenario.ComposableScreenshotScenarioRule +import dev.testify.internal.helpers.overrideResourceConfiguration +import dev.testify.samples.flix.R +import dev.testify.samples.flix.presentation.common.model.MoviePresentationModel +import kotlinx.coroutines.Dispatchers +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import java.util.Locale + +class HomeScreenTest { + + @get:Rule + val rule = ComposableScreenshotScenarioRule(exactness = 0.9f) + + @Before + fun before() { + Coil.setImageLoader( + ImageLoader.Builder(InstrumentationRegistry.getInstrumentation().targetContext) + .dispatcher(Dispatchers.Unconfined) + .build() + ) + } + + @Composable + private fun dummyData() = MoviePresentationModel( + id = 1, + title = stringResource(R.string.dummy_title), + overview = stringResource(R.string.dummy_overview), + releaseDateYear = "(1934)", + genre = stringResource(R.string.dummy_genre), + posterUrl = "file:///android_asset/images/posters/the-man-who-knew-too-much-1934.jpg", + backdropUrl = null + ) + + @ScreenshotInstrumentation + @Test + fun homeScreenBottomSheet() { + overrideResourceConfiguration( + locale = Locale.getDefault() + ) + launchActivity().use { scenario -> + rule + .withScenario(scenario) + .setCompose { + HomeScreenBottomSheetBody( + selectedMovie = dummyData() + ) + } + .assertSame() + } + } + + /** + * Demonstrates how to configure right-to-left rendering and capture localized Compose content. + */ + @ScreenshotInstrumentation + @Test + fun homeScreenBottomSheet_RTL() { + overrideResourceConfiguration( + locale = Locale.forLanguageTag("fa") + ) + launchActivity().use { scenario -> + rule + .withScenario(scenario) + .setCompose { + HomeScreenBottomSheetBody( + selectedMovie = dummyData() + ) + } + .assertSame() + } + } +} diff --git a/Samples/Flix/src/debug/res/values-fa/strings.xml b/Samples/Flix/src/debug/res/values-fa/strings.xml new file mode 100644 index 000000000..c04785b27 --- /dev/null +++ b/Samples/Flix/src/debug/res/values-fa/strings.xml @@ -0,0 +1,6 @@ + + + مردی که زیاد می‌دانست + مردی که زیاد می‌دانست (به انگلیسی: The man who knew Too much) نام فیلمی است در ژانر دلهره آور، جنایی و جاسوسی که در سال ۱۹۳۴ توسط آلفرد هیچکاک ساخته شد. از پخته ترین آثار + فیلم دلهره‌آور + diff --git a/Samples/Flix/src/debug/res/values/strings.xml b/Samples/Flix/src/debug/res/values/strings.xml new file mode 100644 index 000000000..a3c201f1f --- /dev/null +++ b/Samples/Flix/src/debug/res/values/strings.xml @@ -0,0 +1,6 @@ + + + The Man Who Knew Too Much + The Man Who Knew Too Much is a 1934 British spy thriller film directed by Alfred Hitchcock for Gaumont-British, starring Leslie Banks and Peter Lorre. + Thriller + diff --git a/Samples/Flix/src/main/java/dev/testify/samples/flix/ui/homescreen/HomeScreen.kt b/Samples/Flix/src/main/java/dev/testify/samples/flix/ui/homescreen/HomeScreen.kt index 7db91c927..eb62170fe 100644 --- a/Samples/Flix/src/main/java/dev/testify/samples/flix/ui/homescreen/HomeScreen.kt +++ b/Samples/Flix/src/main/java/dev/testify/samples/flix/ui/homescreen/HomeScreen.kt @@ -25,11 +25,26 @@ package dev.testify.samples.flix.ui.homescreen -import androidx.compose.foundation.layout.* +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll -import androidx.compose.material3.* -import androidx.compose.runtime.* +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.ModalBottomSheet +import androidx.compose.material3.SheetState +import androidx.compose.material3.Text +import androidx.compose.material3.rememberModalBottomSheetState +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp @@ -42,7 +57,10 @@ import dev.testify.samples.flix.presentation.homescreen.action.HomeScreenViewAct import dev.testify.samples.flix.presentation.homescreen.viewmodel.HomeScreenViewActionHandler import dev.testify.samples.flix.presentation.homescreen.viewmodel.HomeScreenViewModel import dev.testify.samples.flix.presentation.homescreen.viewmodel.HomeScreenViewState -import dev.testify.samples.flix.ui.common.composeables.* +import dev.testify.samples.flix.ui.common.composeables.HeadliningMovieActions +import dev.testify.samples.flix.ui.common.composeables.HorizontalThumbnailStripWithTitle +import dev.testify.samples.flix.ui.common.composeables.MoviePoster +import dev.testify.samples.flix.ui.common.composeables.MovieThumbnail import dev.testify.samples.flix.ui.common.renderer.ScreenState import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.launch @@ -74,9 +92,14 @@ fun HomeScreen( systemActions: SharedFlow, homeScreenViewActionHandler: HomeScreenViewActionHandler ) = when (homeScreenViewState) { - is HomeScreenViewState.LoadedHomeScreenViewState -> LoadedHomeScreen(homeScreenViewState, systemActions, homeScreenViewActionHandler) + is HomeScreenViewState.LoadedHomeScreenViewState -> LoadedHomeScreen( + homeScreenViewState, + systemActions, + homeScreenViewActionHandler + ) } +@OptIn(ExperimentalMaterial3Api::class) @Composable fun LoadedHomeScreen( viewState: HomeScreenViewState.LoadedHomeScreenViewState, @@ -101,7 +124,13 @@ fun HomeScreenContent( HeadliningMovieActions( movie = presentationModel.headliningMovie, modifier = Modifier.padding(bottom = 10.dp), - onViewInfoPressed = { movie -> homeScreenViewActionHandler?.invoke(HomeScreenViewAction.ViewHeadliningMoveInfoPressed(movie)) } + onViewInfoPressed = { movie -> + homeScreenViewActionHandler?.invoke( + HomeScreenViewAction.ViewHeadliningMoveInfoPressed( + movie + ) + ) + } ) NowPlaying(moviesNowPlaying = presentationModel.moviesNowPlaying) { homeScreenViewActionHandler?.invoke(it) @@ -115,68 +144,77 @@ fun HomeScreenContent( @OptIn(ExperimentalMaterial3Api::class) @Composable fun HomeScreenBottomSheet( - systemActions: SharedFlow? + systemActions: SharedFlow?, + sheetState: SheetState = rememberModalBottomSheetState() ) { val selectedMovie = remember { mutableStateOf(null) } - val sheetState = rememberModalBottomSheetState() val scope = rememberCoroutineScope() LaunchedEffect(systemActions) { systemActions?.collect { - when(it) { + when (it) { is HomeScreenSystemAction.ShowMovieDetailBottomSheet -> { selectedMovie.value = it.moviePresentationModel if (selectedMovie.value != null && !sheetState.isVisible) sheetState.show() } + else -> Unit } } } if (sheetState.isVisible) { ModalBottomSheet( - onDismissRequest = { scope.launch { - sheetState.hide() - }}, + onDismissRequest = { + scope.launch { + sheetState.hide() + } + }, sheetState = sheetState, dragHandle = null ) { selectedMovie.value?.let { selectedMovie -> - Column(modifier = Modifier - .fillMaxWidth() - .fillMaxHeight(0.25f) - .padding(15.dp) + HomeScreenBottomSheetBody(selectedMovie) + } + } + } +} + +@Composable +fun HomeScreenBottomSheetBody(selectedMovie: MoviePresentationModel) { + Column( + modifier = Modifier + .fillMaxWidth() + .fillMaxHeight(0.25f) + .padding(15.dp) + ) { + Row( + modifier = Modifier.fillMaxSize() + ) { + MovieThumbnail(selectedMovie) + Column( + modifier = Modifier + .fillMaxHeight() + .padding(horizontal = 10.dp) + ) { + Text( + text = selectedMovie.title, + style = MaterialTheme.typography.titleMedium, + maxLines = 2 + ) + Row( + modifier = Modifier.padding(vertical = 5.dp) ) { - Row( - modifier = Modifier.fillMaxSize() - ) { - MovieThumbnail(selectedMovie) - Column( - modifier = Modifier - .fillMaxHeight() - .padding(horizontal = 10.dp) - ) { - Text( - text = selectedMovie.title, - style = MaterialTheme.typography.titleMedium, - maxLines = 2 - ) - Row( - modifier = Modifier.padding(vertical = 5.dp) - ) { - selectedMovie.releaseDateYear?.let { - Text(text = it, style = MaterialTheme.typography.bodySmall) - } - } - selectedMovie.overview?.let { - Text( - text = it, - style = MaterialTheme.typography.bodySmall, - maxLines = 4 - ) - } - } + selectedMovie.releaseDateYear?.let { + Text(text = it, style = MaterialTheme.typography.bodySmall) } } + selectedMovie.overview?.let { + Text( + text = it, + style = MaterialTheme.typography.bodySmall, + maxLines = 4 + ) + } } } } diff --git a/Samples/Flix/src/main/res/values/strings.xml b/Samples/Flix/src/main/res/values/strings.xml index 0993171d6..7ff624f9c 100644 --- a/Samples/Flix/src/main/res/values/strings.xml +++ b/Samples/Flix/src/main/res/values/strings.xml @@ -22,4 +22,4 @@ View more info regarding movie titled \"%1$s\" View details regarding movie title \"%1$s\" - \ No newline at end of file +