1
+ package com.google.samples.apps.nowinandroid
2
+
3
+ import android.content.Context
4
+ import android.content.res.Configuration
5
+ import androidx.activity.ComponentActivity
6
+ import androidx.compose.runtime.Composable
7
+ import androidx.compose.runtime.CompositionLocalProvider
8
+ import androidx.compose.ui.platform.LocalDensity
9
+ import androidx.compose.ui.test.junit4.createAndroidComposeRule
10
+ import androidx.compose.ui.test.onRoot
11
+ import androidx.compose.ui.unit.Density
12
+ import androidx.test.core.app.ApplicationProvider
13
+ import com.github.takahirom.roborazzi.ComposePreviewTester
14
+ import com.github.takahirom.roborazzi.ExperimentalRoborazziApi
15
+ import com.github.takahirom.roborazzi.captureRoboImage
16
+ import com.google.samples.apps.nowinandroid.core.ui.DelayedPreview
17
+ import org.robolectric.RuntimeEnvironment
18
+ import org.robolectric.Shadows
19
+ import org.robolectric.shadows.ShadowDisplay
20
+ import sergio.sastre.composable.preview.scanner.android.AndroidComposablePreviewScanner
21
+ import sergio.sastre.composable.preview.scanner.android.AndroidPreviewInfo
22
+ import sergio.sastre.composable.preview.scanner.android.screenshotid.AndroidPreviewScreenshotIdBuilder
23
+ import sergio.sastre.composable.preview.scanner.core.preview.ComposablePreview
24
+ import sergio.sastre.composable.preview.scanner.core.preview.getAnnotation
25
+ import kotlin.math.roundToInt
26
+
27
+ @OptIn(ExperimentalRoborazziApi ::class )
28
+ class MyComposePreviewTester : ComposePreviewTester <AndroidPreviewInfo > {
29
+ val composeTestRule = createAndroidComposeRule<ComponentActivity >()
30
+ override fun options (): ComposePreviewTester .Options {
31
+ val testLifecycleOptions = ComposePreviewTester .Options .JUnit4TestLifecycleOptions (
32
+ testRuleFactory = { composeTestRule }
33
+ )
34
+ return super .options().copy(testLifecycleOptions = testLifecycleOptions)
35
+ }
36
+
37
+ override fun previews (): List <ComposablePreview <AndroidPreviewInfo >> {
38
+ val options = options()
39
+ return AndroidComposablePreviewScanner ()
40
+ .scanPackageTrees(* options.scanOptions.packages.toTypedArray())
41
+ .includeAnnotationInfoForAllOf(DelayedPreview ::class .java)
42
+ .getPreviews()
43
+ }
44
+
45
+ override fun test (preview : ComposablePreview <AndroidPreviewInfo >) {
46
+ val delay = preview.getAnnotation<DelayedPreview >()?.delay ? : 0L
47
+ val previewScannerFileName =
48
+ AndroidPreviewScreenshotIdBuilder (preview).build()
49
+ val fileName =
50
+ if (delay == 0L ) previewScannerFileName else " ${previewScannerFileName} _delay$delay "
51
+ val filePath = " $fileName .png"
52
+ preview.myApplyToRobolectricConfiguration()
53
+ composeTestRule.activityRule.scenario.recreate()
54
+ composeTestRule.apply {
55
+ try {
56
+ if (delay != 0L ) {
57
+ mainClock.autoAdvance = false
58
+ }
59
+ setContent {
60
+ ApplyToCompositionLocal (preview) {
61
+ preview()
62
+ }
63
+ }
64
+ if (delay != 0L ) {
65
+ mainClock.advanceTimeBy(delay)
66
+ }
67
+ onRoot().captureRoboImage(filePath = filePath)
68
+ } finally {
69
+ mainClock.autoAdvance = true
70
+ }
71
+ }
72
+ }
73
+ }
74
+
75
+ @Composable
76
+ fun ApplyToCompositionLocal (
77
+ preview : ComposablePreview <AndroidPreviewInfo >,
78
+ content : @Composable () -> Unit
79
+ ) {
80
+ val fontScale = preview.previewInfo.fontScale
81
+ val density = LocalDensity .current
82
+ val customDensity =
83
+ Density (density = density.density, fontScale = density.fontScale * fontScale)
84
+ CompositionLocalProvider (LocalDensity provides customDensity) {
85
+ content()
86
+ }
87
+
88
+ }
89
+
90
+
91
+ fun ComposablePreview<AndroidPreviewInfo>.myApplyToRobolectricConfiguration () {
92
+ val preview = this
93
+ // ナイトモード
94
+ when (preview.previewInfo.uiMode and Configuration .UI_MODE_NIGHT_MASK ) {
95
+ Configuration .UI_MODE_NIGHT_YES -> RuntimeEnvironment .setQualifiers(" +night" )
96
+ Configuration .UI_MODE_NIGHT_NO -> RuntimeEnvironment .setQualifiers(" +notnight" )
97
+ else -> { /* do nothing */
98
+ }
99
+ }
100
+
101
+ // 画面サイズ
102
+ if (preview.previewInfo.widthDp != - 1 && preview.previewInfo.heightDp != - 1 ) {
103
+ setDisplaySize(preview.previewInfo.widthDp, preview.previewInfo.heightDp)
104
+ }
105
+ }
106
+
107
+ private fun setDisplaySize (widthDp : Int , heightDp : Int ) {
108
+ val context = ApplicationProvider .getApplicationContext<Context >()
109
+ val display = ShadowDisplay .getDefaultDisplay()
110
+ val density = context.resources.displayMetrics.density
111
+ widthDp.let {
112
+ val widthPx = (widthDp * density).roundToInt()
113
+ Shadows .shadowOf(display).setWidth(widthPx)
114
+ }
115
+ heightDp.let {
116
+ val heightPx = (heightDp * density).roundToInt()
117
+ Shadows .shadowOf(display).setHeight(heightPx)
118
+ }
119
+ }
0 commit comments