-
Notifications
You must be signed in to change notification settings - Fork 200
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #56 from DroidKaigi/takahirom/introduce-compose-pr…
…eview-scanner/2024-06-17 Introduce compose preview scanner
- Loading branch information
Showing
13 changed files
with
28 additions
and
184 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
139 changes: 23 additions & 116 deletions
139
app-android/src/test/java/io/github/droidkaigi/confsched/PreviewTest.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,143 +1,50 @@ | ||
package io.github.droidkaigi.confsched | ||
|
||
import android.content.res.Configuration | ||
import android.os.LocaleList | ||
import androidx.compose.runtime.Composable | ||
import androidx.compose.runtime.CompositionLocalProvider | ||
import androidx.compose.ui.platform.LocalConfiguration | ||
import com.airbnb.android.showkase.models.Showkase | ||
import com.airbnb.android.showkase.models.ShowkaseBrowserComponent | ||
import com.github.takahirom.roborazzi.DEFAULT_ROBORAZZI_OUTPUT_DIR_PATH | ||
import com.github.takahirom.roborazzi.captureRoboImage | ||
import io.github.droidkaigi.confsched.designsystem.preview.MultiLanguagePreviewDefinition | ||
import io.github.droidkaigi.confsched.designsystem.preview.MultiThemePreviewDefinition | ||
import io.github.droidkaigi.confsched.designsystem.preview.ShowkaseMultiplePreviewsWorkaround | ||
import org.junit.Test | ||
import org.junit.runner.RunWith | ||
import org.robolectric.ParameterizedRobolectricTestRunner | ||
import java.util.Locale | ||
import org.robolectric.RuntimeEnvironment | ||
import sergio.sastre.composable.preview.scanner.android.AndroidComposablePreviewScanner | ||
import sergio.sastre.composable.preview.scanner.android.AndroidPreviewInfo | ||
import sergio.sastre.composable.preview.scanner.core.preview.ComposablePreview | ||
|
||
@RunWith(ParameterizedRobolectricTestRunner::class) | ||
class PreviewTest( | ||
val showkaseBrowserComponent: ShowkaseBrowserComponent, | ||
private val preview: ComposablePreview<AndroidPreviewInfo>, | ||
) { | ||
object RobolectricPreviewInfosApplier { | ||
fun applyFor(preview: ComposablePreview<AndroidPreviewInfo>) { | ||
val uiMode = | ||
when (preview.previewInfo.uiMode and Configuration.UI_MODE_NIGHT_MASK == Configuration.UI_MODE_NIGHT_YES) { | ||
true -> "+night" | ||
false -> "+notnight" | ||
} | ||
RuntimeEnvironment.setQualifiers(uiMode) | ||
} | ||
} | ||
|
||
@Test | ||
fun previewScreenshot() { | ||
val filePath = | ||
DEFAULT_ROBORAZZI_OUTPUT_DIR_PATH + "/" + showkaseBrowserComponent.componentKey + ".png" | ||
DEFAULT_ROBORAZZI_OUTPUT_DIR_PATH + "/" + preview.methodName + ".png" | ||
RobolectricPreviewInfosApplier.applyFor(preview) | ||
captureRoboImage( | ||
filePath, | ||
filePath = filePath, | ||
) { | ||
ProvidesPreviewValues(group = showkaseBrowserComponent.group, componentKey = showkaseBrowserComponent.componentKey) { | ||
showkaseBrowserComponent.component() | ||
} | ||
} | ||
} | ||
|
||
@Suppress("TestFunctionName") | ||
@ShowkaseMultiplePreviewsWorkaround | ||
@Composable | ||
private fun ProvidesPreviewValues(group: String, componentKey: String, content: @Composable () -> Unit) { | ||
val appliers = arrayListOf<(Configuration) -> Unit>() | ||
|
||
if (isCustomGroup(group = group)) { | ||
val previewValue = extractPreviewValues(group = group, componentKey) | ||
|
||
when (group) { | ||
MultiLanguagePreviewDefinition.Group -> { | ||
appliers += { c -> | ||
c.setLocales(newLocales(baseLocales = c.locales, previewValue = previewValue)) | ||
} | ||
} | ||
MultiThemePreviewDefinition.Group -> { | ||
appliers += { c -> | ||
c.uiMode = newUiMode(baseUiMode = c.uiMode, previewValue = previewValue) | ||
} | ||
} | ||
} | ||
} | ||
|
||
val newConfiguration = appliers.fold(LocalConfiguration.current) { c, a -> c.apply(a) } | ||
|
||
CompositionLocalProvider(LocalConfiguration provides newConfiguration) { | ||
// Notify locale changes to lang() through the following invocation. | ||
LocaleList.setDefault(LocalConfiguration.current.locales) | ||
|
||
content() | ||
} | ||
} | ||
|
||
/** | ||
* Depends on the naming rule from Showkase. | ||
* We must not include "_${group}_" in an original preview function name. | ||
*/ | ||
@ShowkaseMultiplePreviewsWorkaround | ||
private fun extractPreviewValues(group: String, componentKey: String): String { | ||
val components = componentKey.split("_") | ||
|
||
// _${group_ is expected here | ||
val groupIndex = requireNotNull(components.indexOf(group).takeIf { it > 0 }) { | ||
"Failed to extract a preview value for $group: $group is not found in $components" | ||
} | ||
|
||
val modifiedPreviewName = requireNotNull(components.getOrNull(groupIndex + 1)) { | ||
"Failed to extract a preview value for $group: $components is unexpectedly aligned" | ||
} | ||
|
||
// ${preview_name}_${preview_value}_${...others} | ||
val match = requireNotNull(Regex("\\w+-([\\w-]+)-[_\\w]+").matchEntire(modifiedPreviewName)) { | ||
"Failed to extract a preview value for $group: no value was found in $modifiedPreviewName" | ||
} | ||
|
||
return requireNotNull(match.groupValues.getOrNull(1)) { | ||
"Failed to extract a preview value for $group: this may be a development issue" | ||
preview() | ||
} | ||
} | ||
|
||
@ShowkaseMultiplePreviewsWorkaround | ||
private fun newLocales(baseLocales: LocaleList, previewValue: String): LocaleList { | ||
val locale = when (previewValue) { | ||
MultiLanguagePreviewDefinition.English.Name -> { | ||
MultiLanguagePreviewDefinition.English.Locale | ||
} | ||
MultiLanguagePreviewDefinition.Japanese.Name -> { | ||
MultiLanguagePreviewDefinition.Japanese.Locale | ||
} | ||
else -> return baseLocales | ||
} | ||
|
||
return LocaleList(Locale.getAvailableLocales().first { it.toString() == locale }) | ||
} | ||
|
||
@ShowkaseMultiplePreviewsWorkaround | ||
private fun newUiMode(baseUiMode: Int, previewValue: String): Int { | ||
val nightMode = when (previewValue) { | ||
// FIXME | ||
// MultiThemePreviewDefinition.DarkMode.Name -> { | ||
// MultiThemePreviewDefinition.DarkMode.UiMode | ||
// } | ||
// MultiThemePreviewDefinition.LightMode.Name -> { | ||
// MultiThemePreviewDefinition.LightMode.UiMode | ||
// } | ||
else -> baseUiMode | ||
} | ||
|
||
val currentNightMode = baseUiMode and Configuration.UI_MODE_NIGHT_MASK | ||
return baseUiMode xor currentNightMode or nightMode | ||
} | ||
|
||
companion object { | ||
fun isCustomGroup(group: String): Boolean { | ||
return group != "Default Group" | ||
} | ||
|
||
@ParameterizedRobolectricTestRunner.Parameters | ||
@JvmStatic | ||
fun components(): Iterable<Array<Any?>> { | ||
return Showkase.getMetadata().componentList.map { showkaseBrowserComponent -> | ||
arrayOf(showkaseBrowserComponent) | ||
} | ||
fun components(): List<ComposablePreview<AndroidPreviewInfo>> { | ||
return AndroidComposablePreviewScanner() | ||
.scanPackageTrees("io.github.droidkaigi.confsched") | ||
.getPreviews() | ||
} | ||
} | ||
} |
7 changes: 0 additions & 7 deletions
7
app-android/src/test/java/io/github/droidkaigi/confsched/ShowkaseRootModule.kt
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
26 changes: 0 additions & 26 deletions
26
...ogic/src/main/kotlin/io/github/droidkaigi/confsched/primitive/KmpAndroidShowkasePlugin.kt
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters