Skip to content

Commit

Permalink
Merge pull request #1224 from DroidKaigi/kenken/pass_clock_to_control…
Browse files Browse the repository at this point in the history
…l_time_in_preview

[Android] add LocalClock to control time for screenshot test
  • Loading branch information
takahirom authored Sep 30, 2023
2 parents 1a9febb + d41ed3f commit 9559d15
Show file tree
Hide file tree
Showing 10 changed files with 109 additions and 41 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import io.github.droidkaigi.confsched2023.data.di.AppAndroidBuildConfig
import io.github.droidkaigi.confsched2023.model.BuildConfigProvider
import kotlinx.datetime.Clock
import javax.inject.Singleton

@InstallIn(SingletonComponent::class)
Expand All @@ -15,9 +16,19 @@ class AppModule {
@Singleton
@AppAndroidBuildConfig
fun provideBuildConfigProvider(): BuildConfigProvider = AppBuildConfigProvider()

@Provides
@Singleton
fun provideClockProvider(): ClockProvider = object : ClockProvider {
override fun clock(): Clock = Clock.System
}
}

class AppBuildConfigProvider(
override val versionName: String = BuildConfig.VERSION_NAME,
override val debugBuild: Boolean = BuildConfig.DEBUG,
) : BuildConfigProvider

interface ClockProvider {
fun clock(): Clock
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import androidx.activity.enableEdgeToEdge
import androidx.compose.material3.windowsizeclass.ExperimentalMaterial3WindowSizeClassApi
import androidx.compose.material3.windowsizeclass.calculateWindowSizeClass
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.getValue
import androidx.compose.runtime.produceState
import androidx.compose.runtime.remember
Expand All @@ -19,12 +20,18 @@ import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
import androidx.window.layout.DisplayFeature
import androidx.window.layout.WindowInfoTracker
import dagger.hilt.android.AndroidEntryPoint
import io.github.droidkaigi.confsched2023.ui.compositionlocal.LocalClock
import kotlinx.collections.immutable.PersistentList
import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toPersistentList
import javax.inject.Inject

@AndroidEntryPoint
class MainActivity : ComponentActivity() {

@Inject
lateinit var clockProvider: ClockProvider

@OptIn(ExperimentalMaterial3WindowSizeClassApi::class)
override fun onCreate(savedInstanceState: Bundle?) {
installSplashScreen()
Expand Down Expand Up @@ -55,10 +62,12 @@ class MainActivity : ComponentActivity() {
setContent {
val windowSize = calculateWindowSizeClass(this)
val displayFeatures = calculateDisplayFeatures(this)
KaigiApp(
windowSize = windowSize,
displayFeatures = displayFeatures,
)
CompositionLocalProvider(LocalClock provides clockProvider.clock()) {
KaigiApp(
windowSize = windowSize,
displayFeatures = displayFeatures,
)
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package io.github.droidkaigi.confsched2023

import dagger.Module
import dagger.Provides
import dagger.hilt.components.SingletonComponent
import dagger.hilt.testing.TestInstallIn
import kotlinx.datetime.Clock
import kotlinx.datetime.Instant
import javax.inject.Singleton

@TestInstallIn(
components = [SingletonComponent::class],
replaces = [AppModule::class],
)
@Module
class FakeAppModule {
@Provides
@Singleton
fun provideClockProvider(): ClockProvider = object : ClockProvider {
override fun clock(): Clock = object : Clock {
override fun now() = Instant.parse("2023-09-14T10:00:00.00Z")
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -81,14 +81,11 @@ public enum class DroidKaigi2023Day(
/**
* @return appropriate initial day for now
*/
fun initialSelectedDay(isTest: Boolean = false): DroidKaigi2023Day {
// Timetable tab set initial tab with current date.
// To get the consistent test result, fix selected timetable tab to Day1 here.
if (isTest) return Day1
fun initialSelectedDay(clock: Clock): DroidKaigi2023Day {
val reversedEntries = entries.sortedByDescending { it.day }
var selectedDay = reversedEntries.last()
for (entry in reversedEntries) {
if (Clock.System.now() <= entry.end) selectedDay = entry
if (clock.now() <= entry.end) selectedDay = entry
}
return selectedDay
}
Expand Down
1 change: 1 addition & 0 deletions core/testing/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ dependencies {
implementation(projects.core.model)
implementation(projects.core.designsystem)
implementation(projects.core.data)
implementation(projects.core.ui)
implementation(projects.feature.main)
implementation(projects.feature.sessions)
implementation(projects.feature.about)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package io.github.droidkaigi.confsched2023.testing.robot

import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.hasTestTag
import androidx.compose.ui.test.isRoot
Expand All @@ -25,6 +26,8 @@ import io.github.droidkaigi.confsched2023.sessions.component.TimetableUiTypeChan
import io.github.droidkaigi.confsched2023.sessions.section.TimetableTabTestTag
import io.github.droidkaigi.confsched2023.testing.RobotTestRule
import io.github.droidkaigi.confsched2023.testing.coroutines.runTestWithLogging
import io.github.droidkaigi.confsched2023.ui.compositionlocal.FakeClock
import io.github.droidkaigi.confsched2023.ui.compositionlocal.LocalClock
import kotlinx.coroutines.test.TestDispatcher
import javax.inject.Inject
import kotlin.time.Duration.Companion.seconds
Expand All @@ -50,12 +53,14 @@ class TimetableScreenRobot @Inject constructor(

fun setupTimetableScreenContent() {
composeTestRule.setContent {
KaigiTheme {
TimetableScreen(
onSearchClick = { },
onTimetableItemClick = { },
onBookmarkIconClick = { },
)
CompositionLocalProvider(LocalClock provides FakeClock) {
KaigiTheme {
TimetableScreen(
onSearchClick = { },
onTimetableItemClick = { },
onBookmarkIconClick = { },
)
}
}
}
waitUntilIdle()
Expand Down
1 change: 1 addition & 0 deletions core/ui/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ kotlin {
implementation(libs.kermit)
api(projects.core.common)
api(libs.composeImageLoader)
api(libs.kotlinxDatetime)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package io.github.droidkaigi.confsched2023.ui.compositionlocal

import androidx.compose.runtime.staticCompositionLocalOf
import kotlinx.datetime.Clock
import kotlinx.datetime.Instant

@Suppress("CompositionLocalAllowlist")
val LocalClock = staticCompositionLocalOf<Clock> {
Clock.System
}

object FakeClock : Clock {
override fun now(): Instant = Instant.parse("2023-09-14T10:00:00.000Z")
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import androidx.compose.material3.Scaffold
import androidx.compose.material3.SnackbarHost
import androidx.compose.material3.SnackbarHostState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.ReadOnlyComposable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
Expand Down Expand Up @@ -51,6 +52,8 @@ import io.github.droidkaigi.confsched2023.sessions.section.TimetableListUiState
import io.github.droidkaigi.confsched2023.sessions.section.TimetableSheet
import io.github.droidkaigi.confsched2023.sessions.section.TimetableSheetUiState
import io.github.droidkaigi.confsched2023.ui.SnackbarMessageEffect
import io.github.droidkaigi.confsched2023.ui.compositionlocal.FakeClock
import io.github.droidkaigi.confsched2023.ui.compositionlocal.LocalClock
import kotlinx.collections.immutable.toPersistentMap
import kotlin.math.roundToInt

Expand Down Expand Up @@ -249,32 +252,34 @@ private fun TimetableScreen(
@MultiThemePreviews
@Composable
fun PreviewTimetableScreenDark() {
KaigiTheme {
TimetableScreen(
uiState = TimetableScreenUiState(
contentUiState = TimetableSheetUiState.ListTimetable(
mapOf(
DroidKaigi2023Day.Day1 to TimetableListUiState(
mapOf<String, List<TimetableItem>>().toPersistentMap(),
Timetable(),
),
DroidKaigi2023Day.Day2 to TimetableListUiState(
mapOf<String, List<TimetableItem>>().toPersistentMap(),
Timetable(),
CompositionLocalProvider(LocalClock provides FakeClock) {
KaigiTheme {
TimetableScreen(
uiState = TimetableScreenUiState(
contentUiState = TimetableSheetUiState.ListTimetable(
mapOf(
DroidKaigi2023Day.Day1 to TimetableListUiState(
mapOf<String, List<TimetableItem>>().toPersistentMap(),
Timetable(),
),
DroidKaigi2023Day.Day2 to TimetableListUiState(
mapOf<String, List<TimetableItem>>().toPersistentMap(),
Timetable(),
),
),
),
timetableUiType = TimetableUiType.Grid,
onBookmarkIconClickStatus = false,
),
timetableUiType = TimetableUiType.Grid,
onBookmarkIconClickStatus = false,
),
snackbarHostState = SnackbarHostState(),
onTimetableItemClick = {},
onBookmarkClick = { _, _ -> },
onBookmarkIconClick = {},
onSearchClick = {},
onTimetableUiChangeClick = {},
onReachAnimationEnd = {},
modifier = Modifier.statusBarsPadding(),
)
snackbarHostState = SnackbarHostState(),
onTimetableItemClick = {},
onBookmarkClick = { _, _ -> },
onBookmarkIconClick = {},
onSearchClick = {},
onTimetableUiChangeClick = {},
onReachAnimationEnd = {},
modifier = Modifier.statusBarsPadding(),
)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ import io.github.droidkaigi.confsched2023.sessions.component.rememberTimetableTa
import io.github.droidkaigi.confsched2023.sessions.section.TimetableSheetUiState.Empty
import io.github.droidkaigi.confsched2023.sessions.section.TimetableSheetUiState.GridTimetable
import io.github.droidkaigi.confsched2023.sessions.section.TimetableSheetUiState.ListTimetable
import io.github.droidkaigi.confsched2023.ui.isTest
import io.github.droidkaigi.confsched2023.ui.compositionlocal.LocalClock

const val TimetableTabTestTag = "TimetableTab"

Expand All @@ -60,7 +60,8 @@ fun TimetableSheet(
contentPadding: PaddingValues,
modifier: Modifier = Modifier,
) {
var selectedDay by rememberSaveable { mutableStateOf(DroidKaigi2023Day.initialSelectedDay(isTest())) }
val clock = LocalClock.current
var selectedDay by rememberSaveable { mutableStateOf(DroidKaigi2023Day.initialSelectedDay(clock)) }
val corner by animateIntAsState(
if (timetableScreenScrollState.isScreenLayoutCalculating || timetableScreenScrollState.isSheetExpandable) 40 else 0,
label = "Timetable corner state",
Expand Down

0 comments on commit 9559d15

Please sign in to comment.