From 1a19a7a1eb99636f79786c570daa3f639d938cd2 Mon Sep 17 00:00:00 2001 From: Geoff Powell Date: Fri, 23 Aug 2024 15:24:26 -0400 Subject: [PATCH] Add preview param support --- paparazzi-annotations/README.md | 114 +++++++++++++++++ .../api/paparazzi-annotations.api | 93 ++++++++++++-- paparazzi-annotations/build.gradle | 1 + .../annotations/PaparazziPreviewData.kt | 105 +++++++++++++++- paparazzi-gradle-plugin/build.gradle | 1 + .../cash/paparazzi/gradle/PaparazziPlugin.kt | 10 ++ .../app/cash/paparazzi/gradle/PreviewTests.kt | 4 +- .../paparazzi/gradle/utils/PreviewUtils.kt | 53 ++------ .../paparazzi/gradle/PaparazziPluginTest.kt | 17 +-- ...preview[HelloPaparazzi_HelloPaparazzi].png | Bin 4514 -> 4073 bytes .../build.gradle | 33 ----- .../paparazzi/plugin/test/HelloPaparazzi.kt | 22 ---- .../paparazzi/plugin/test/HelloPaparazzi.kt | 37 ------ ...zzi_HelloPaparazziParameterized,text0].png | Bin 0 -> 2995 bytes ...zzi_HelloPaparazziParameterized,text1].png | Bin 0 -> 2910 bytes ...viewConfig,fs_1.5,Dark,Normal,Nexus 6].png | Bin 0 -> 4787 bytes ...Paparazzi_HelloPaparazziPreviewConfig].png | Bin 4583 -> 0 bytes ...preview[HelloPaparazzi_HelloPaparazzi].png | Bin 2535 -> 2410 bytes .../api/paparazzi-preview-processor.api | 4 + .../preview/processor/PaparazziPoet.kt | 116 ++++++++++++++---- .../cash/paparazzi/preview/processor/Utils.kt | 40 +++++- .../cash/paparazzi/sample/HelloPaparazzi.kt | 45 +++++++ 22 files changed, 511 insertions(+), 184 deletions(-) create mode 100644 paparazzi-annotations/README.md delete mode 100644 paparazzi-gradle-plugin/src/test/projects/preview-annotation-preview-parameters/build.gradle delete mode 100644 paparazzi-gradle-plugin/src/test/projects/preview-annotation-preview-parameters/src/main/java/app/cash/paparazzi/plugin/test/HelloPaparazzi.kt create mode 100644 paparazzi-gradle-plugin/src/test/projects/preview-annotation-sample/src/test/snapshots/images/app.cash.paparazzi.plugin.test_PreviewTests_preview[HelloPaparazzi_HelloPaparazziParameterized,text0].png create mode 100644 paparazzi-gradle-plugin/src/test/projects/preview-annotation-sample/src/test/snapshots/images/app.cash.paparazzi.plugin.test_PreviewTests_preview[HelloPaparazzi_HelloPaparazziParameterized,text1].png create mode 100644 paparazzi-gradle-plugin/src/test/projects/preview-annotation-sample/src/test/snapshots/images/app.cash.paparazzi.plugin.test_PreviewTests_preview[HelloPaparazzi_HelloPaparazziPreviewConfig,fs_1.5,Dark,Normal,Nexus 6].png delete mode 100644 paparazzi-gradle-plugin/src/test/projects/preview-annotation-sample/src/test/snapshots/images/app.cash.paparazzi.plugin.test_PreviewTests_preview[HelloPaparazzi_HelloPaparazziPreviewConfig].png diff --git a/paparazzi-annotations/README.md b/paparazzi-annotations/README.md new file mode 100644 index 0000000000..dddff45184 --- /dev/null +++ b/paparazzi-annotations/README.md @@ -0,0 +1,114 @@ +# `@Paparazzi` +An annotation used to generate Paparazzi snapshots for composable preview functions. + +## Installation +Add the following to your `build.gradle` file + +```groovy +apply plugin: 'app.cash.paparazzi.preview' +``` + +## Basic Usage +Apply the annotation alongside an existing preview method. The annotation processor will generate a manifest of information about this method and the previews applied. + +```kotlin +import app.cash.paparazzi.preview.Paparazzi + +@Paparazzi +@Preview +@Composable +fun MyViewPreview() { + MyView(title = "Hello, Paparazzi Annotation") +} +``` + +Run `:recordPaparazziDebug` in your module to generate preview snapshots (and optionally verify them using `:verifyPaparazziDebug`) as you normally would. + +A test class to generate snapshots for annotated previews will automatically be generated. +If you prefer to define a custom snapshot test, you mey disable test generation by adding the following to your `build.gradle` file. + +```groovy +paparazziPreview { + generateTestClass = false +} +``` + +You may implement your own test class, as shown below, to create snapshots for all previews included in the generated manifest (`paparazziAnnotations`). + +```kotlin +import app.cash.paparazzi.Paparazzi +import app.cash.paparazzi.preview.PaparazziPreviewData +import app.cash.paparazzi.preview.PaparazziValuesProvider +import app.cash.paparazzi.preview.deviceConfig +import app.cash.paparazzi.preview.snapshot +import com.android.ide.common.rendering.api.SessionParams.RenderingMode.SHRINK +import com.google.testing.junit.testparameterinjector.TestParameter +import com.google.testing.junit.testparameterinjector.TestParameterInjector +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(TestParameterInjector::class) +class PreviewTests( + @TestParameter(valuesProvider = PreviewConfigValuesProvider::class) + private val preview: PaparazziPreviewData, +) { + private class PreviewConfigValuesProvider : PaparazziValuesProvider(paparazziPreviews) + + @get:Rule + val paparazzi = Paparazzi( + deviceConfig = preview.deviceConfig(), + renderingMode = SHRINK, + ) + + @Test + fun preview() { + paparazzi.snapshot(preview) + } +} +``` + +## Preview Parameter +If your preview function accepts a parameter using `@PreviewParameter`, then snapshots will be created for each combination of preview / param. + +```kotlin +@Paparazzi +@Preview +@Composable +fun MyViewPreview(@PreviewParameter(MyTitleProvider::class) title: String) { + MyView(title = title) +} + +class MyTitleProvider : PreviewParameterProvider { + override val values = sequenceOf("Hello", "Paparazzi", "Annotation") +} +``` + +## Composable Wrapping +If you need to apply additional UI treatment around your previews, you may provide a composable wrapper within the test. + +```kotlin +paparazzi.snapshot(preview) { content -> + Box(modifier = Modifier.background(Color.Gray)) { + content() + } +} +``` + +## Preview Composition +If you have multiple preview annotations applied to a function, or have them nested behind a custom annotation, they will all be included in the snapshot manifest. + +```kotlin +@Paparazzi +@ScaledThemedPreviews +@Composable +fun MyViewPreview() { + MyView(title = "Hello, Paparazzi Annotation") +} + +@Preview(name = "small light", fontScale = 1f, uiMode = Configuration.UI_MODE_NIGHT_NO, device = PIXEL_3_XL) +@Preview(name = "small dark", fontScale = 1f, uiMode = Configuration.UI_MODE_NIGHT_YES, device = PIXEL_3_XL) +@Preview(name = "large light", fontScale = 2f, uiMode = Configuration.UI_MODE_NIGHT_NO, device = PIXEL_3_XL) +@Preview(name = "large dark", fontScale = 2f, uiMode = Configuration.UI_MODE_NIGHT_YES, device = PIXEL_3_XL) +annotation class ScaledThemedPreviews +``` diff --git a/paparazzi-annotations/api/paparazzi-annotations.api b/paparazzi-annotations/api/paparazzi-annotations.api index 913e18bbf8..4274755205 100644 --- a/paparazzi-annotations/api/paparazzi-annotations.api +++ b/paparazzi-annotations/api/paparazzi-annotations.api @@ -5,19 +5,23 @@ public abstract interface class app/cash/paparazzi/annotations/PaparazziPreviewD } public final class app/cash/paparazzi/annotations/PaparazziPreviewData$Default : app/cash/paparazzi/annotations/PaparazziPreviewData { - public fun (Ljava/lang/String;Lkotlin/jvm/functions/Function0;)V + public static final field $stable I + public fun (Ljava/lang/String;Lapp/cash/paparazzi/annotations/PreviewData;Lkotlin/jvm/functions/Function2;)V public final fun component1 ()Ljava/lang/String; - public final fun component2 ()Lkotlin/jvm/functions/Function0; - public final fun copy (Ljava/lang/String;Lkotlin/jvm/functions/Function0;)Lapp/cash/paparazzi/annotations/PaparazziPreviewData$Default; - public static synthetic fun copy$default (Lapp/cash/paparazzi/annotations/PaparazziPreviewData$Default;Ljava/lang/String;Lkotlin/jvm/functions/Function0;ILjava/lang/Object;)Lapp/cash/paparazzi/annotations/PaparazziPreviewData$Default; + public final fun component2 ()Lapp/cash/paparazzi/annotations/PreviewData; + public final fun component3 ()Lkotlin/jvm/functions/Function2; + public final fun copy (Ljava/lang/String;Lapp/cash/paparazzi/annotations/PreviewData;Lkotlin/jvm/functions/Function2;)Lapp/cash/paparazzi/annotations/PaparazziPreviewData$Default; + public static synthetic fun copy$default (Lapp/cash/paparazzi/annotations/PaparazziPreviewData$Default;Ljava/lang/String;Lapp/cash/paparazzi/annotations/PreviewData;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lapp/cash/paparazzi/annotations/PaparazziPreviewData$Default; public fun equals (Ljava/lang/Object;)Z - public final fun getComposable ()Lkotlin/jvm/functions/Function0; + public final fun getComposable ()Lkotlin/jvm/functions/Function2; + public final fun getPreview ()Lapp/cash/paparazzi/annotations/PreviewData; public final fun getSnapshotName ()Ljava/lang/String; public fun hashCode ()I public fun toString ()Ljava/lang/String; } public final class app/cash/paparazzi/annotations/PaparazziPreviewData$Empty : app/cash/paparazzi/annotations/PaparazziPreviewData { + public static final field $stable I public static final field INSTANCE Lapp/cash/paparazzi/annotations/PaparazziPreviewData$Empty; public fun equals (Ljava/lang/Object;)Z public fun hashCode ()I @@ -25,15 +29,86 @@ public final class app/cash/paparazzi/annotations/PaparazziPreviewData$Empty : a } public final class app/cash/paparazzi/annotations/PaparazziPreviewData$Error : app/cash/paparazzi/annotations/PaparazziPreviewData { - public fun (Ljava/lang/String;Ljava/lang/String;)V + public static final field $stable I + public fun (Ljava/lang/String;Lapp/cash/paparazzi/annotations/PreviewData;Ljava/lang/String;)V public final fun component1 ()Ljava/lang/String; - public final fun component2 ()Ljava/lang/String; - public final fun copy (Ljava/lang/String;Ljava/lang/String;)Lapp/cash/paparazzi/annotations/PaparazziPreviewData$Error; - public static synthetic fun copy$default (Lapp/cash/paparazzi/annotations/PaparazziPreviewData$Error;Ljava/lang/String;Ljava/lang/String;ILjava/lang/Object;)Lapp/cash/paparazzi/annotations/PaparazziPreviewData$Error; + public final fun component2 ()Lapp/cash/paparazzi/annotations/PreviewData; + public final fun component3 ()Ljava/lang/String; + public final fun copy (Ljava/lang/String;Lapp/cash/paparazzi/annotations/PreviewData;Ljava/lang/String;)Lapp/cash/paparazzi/annotations/PaparazziPreviewData$Error; + public static synthetic fun copy$default (Lapp/cash/paparazzi/annotations/PaparazziPreviewData$Error;Ljava/lang/String;Lapp/cash/paparazzi/annotations/PreviewData;Ljava/lang/String;ILjava/lang/Object;)Lapp/cash/paparazzi/annotations/PaparazziPreviewData$Error; public fun equals (Ljava/lang/Object;)Z public final fun getMessage ()Ljava/lang/String; + public final fun getPreview ()Lapp/cash/paparazzi/annotations/PreviewData; public final fun getSnapshotName ()Ljava/lang/String; public fun hashCode ()I public fun toString ()Ljava/lang/String; } +public final class app/cash/paparazzi/annotations/PaparazziPreviewData$Provider : app/cash/paparazzi/annotations/PaparazziPreviewData { + public static final field $stable I + public fun (Ljava/lang/String;Lapp/cash/paparazzi/annotations/PreviewData;Lkotlin/jvm/functions/Function3;Lapp/cash/paparazzi/annotations/PreviewParameterData;)V + public final fun component1 ()Ljava/lang/String; + public final fun component2 ()Lapp/cash/paparazzi/annotations/PreviewData; + public final fun component3 ()Lkotlin/jvm/functions/Function3; + public final fun component4 ()Lapp/cash/paparazzi/annotations/PreviewParameterData; + public final fun copy (Ljava/lang/String;Lapp/cash/paparazzi/annotations/PreviewData;Lkotlin/jvm/functions/Function3;Lapp/cash/paparazzi/annotations/PreviewParameterData;)Lapp/cash/paparazzi/annotations/PaparazziPreviewData$Provider; + public static synthetic fun copy$default (Lapp/cash/paparazzi/annotations/PaparazziPreviewData$Provider;Ljava/lang/String;Lapp/cash/paparazzi/annotations/PreviewData;Lkotlin/jvm/functions/Function3;Lapp/cash/paparazzi/annotations/PreviewParameterData;ILjava/lang/Object;)Lapp/cash/paparazzi/annotations/PaparazziPreviewData$Provider; + public fun equals (Ljava/lang/Object;)Z + public final fun getComposable ()Lkotlin/jvm/functions/Function3; + public final fun getPreview ()Lapp/cash/paparazzi/annotations/PreviewData; + public final fun getPreviewParameter ()Lapp/cash/paparazzi/annotations/PreviewParameterData; + public final fun getSnapshotName ()Ljava/lang/String; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; + public final fun withPreviewParameterIndex (I)Lapp/cash/paparazzi/annotations/PaparazziPreviewData$Provider; +} + +public final class app/cash/paparazzi/annotations/PaparazziPreviewDefaults { + public static final field $stable I + public static final field DEVICE_ID Ljava/lang/String; + public static final field INSTANCE Lapp/cash/paparazzi/annotations/PaparazziPreviewDefaults; +} + +public final class app/cash/paparazzi/annotations/PreviewData { + public static final field $stable I + public fun ()V + public fun (Ljava/lang/Float;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/Integer;Ljava/lang/Integer;Ljava/lang/String;Ljava/lang/String;)V + public synthetic fun (Ljava/lang/Float;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/Integer;Ljava/lang/Integer;Ljava/lang/String;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun component1 ()Ljava/lang/Float; + public final fun component2 ()Ljava/lang/String; + public final fun component3 ()Ljava/lang/Integer; + public final fun component4 ()Ljava/lang/Integer; + public final fun component5 ()Ljava/lang/Integer; + public final fun component6 ()Ljava/lang/String; + public final fun component7 ()Ljava/lang/String; + public final fun copy (Ljava/lang/Float;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/Integer;Ljava/lang/Integer;Ljava/lang/String;Ljava/lang/String;)Lapp/cash/paparazzi/annotations/PreviewData; + public static synthetic fun copy$default (Lapp/cash/paparazzi/annotations/PreviewData;Ljava/lang/Float;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/Integer;Ljava/lang/Integer;Ljava/lang/String;Ljava/lang/String;ILjava/lang/Object;)Lapp/cash/paparazzi/annotations/PreviewData; + public fun equals (Ljava/lang/Object;)Z + public final fun getBackgroundColor ()Ljava/lang/String; + public final fun getDevice ()Ljava/lang/String; + public final fun getFontScale ()Ljava/lang/Float; + public final fun getHeightDp ()Ljava/lang/Integer; + public final fun getLocale ()Ljava/lang/String; + public final fun getUiMode ()Ljava/lang/Integer; + public final fun getWidthDp ()Ljava/lang/Integer; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class app/cash/paparazzi/annotations/PreviewParameterData { + public static final field $stable I + public fun (Ljava/lang/String;Lkotlin/sequences/Sequence;I)V + public synthetic fun (Ljava/lang/String;Lkotlin/sequences/Sequence;IILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun component1 ()Ljava/lang/String; + public final fun component2 ()Lkotlin/sequences/Sequence; + public final fun component3 ()I + public final fun copy (Ljava/lang/String;Lkotlin/sequences/Sequence;I)Lapp/cash/paparazzi/annotations/PreviewParameterData; + public static synthetic fun copy$default (Lapp/cash/paparazzi/annotations/PreviewParameterData;Ljava/lang/String;Lkotlin/sequences/Sequence;IILjava/lang/Object;)Lapp/cash/paparazzi/annotations/PreviewParameterData; + public fun equals (Ljava/lang/Object;)Z + public final fun getIndex ()I + public final fun getName ()Ljava/lang/String; + public final fun getValues ()Lkotlin/sequences/Sequence; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + diff --git a/paparazzi-annotations/build.gradle b/paparazzi-annotations/build.gradle index f97b16750b..6a9c7e28e8 100644 --- a/paparazzi-annotations/build.gradle +++ b/paparazzi-annotations/build.gradle @@ -4,4 +4,5 @@ apply plugin: 'com.vanniktech.maven.publish' dependencies { compileOnly libs.compose.runtime + compileOnly libs.tools.layoutlib } diff --git a/paparazzi-annotations/src/main/java/app/cash/paparazzi/annotations/PaparazziPreviewData.kt b/paparazzi-annotations/src/main/java/app/cash/paparazzi/annotations/PaparazziPreviewData.kt index def6232ba6..93e62ec090 100644 --- a/paparazzi-annotations/src/main/java/app/cash/paparazzi/annotations/PaparazziPreviewData.kt +++ b/paparazzi-annotations/src/main/java/app/cash/paparazzi/annotations/PaparazziPreviewData.kt @@ -1,11 +1,17 @@ package app.cash.paparazzi.annotations +import android.content.res.Configuration import androidx.compose.runtime.Composable +public object PaparazziPreviewDefaults { + public const val DEVICE_ID: String = "id:pixel_5" +} + /** * Represents composables annotated with @Paparazzi annotation * * Default - Represents a composable with no parameters + * Provider - Represents a composable with parameters using @PreviewParameter * Empty - Represents a configuration with zero annotated composables * Error - Represents an error state with a message if the composable is misconfgured (ex. private Composable function) */ @@ -13,9 +19,28 @@ public sealed interface PaparazziPreviewData { public data class Default( val snapshotName: String, + val preview: PreviewData, val composable: @Composable () -> Unit ) : PaparazziPreviewData { - override fun toString(): String = snapshotName + override fun toString(): String = buildList { + add(snapshotName) + preview.toString().takeIf { it.isNotEmpty() }?.let(::add) + }.joinToString(",") + } + + public data class Provider( + val snapshotName: String, + val preview: PreviewData, + val composable: @Composable (T) -> Unit, + val previewParameter: PreviewParameterData + ) : PaparazziPreviewData { + override fun toString(): String = buildList { + add(snapshotName) + preview.toString().takeIf { it.isNotEmpty() }?.let(::add) + add(previewParameter.toString()) + }.joinToString(",") + + public fun withPreviewParameterIndex(index: Int): Provider = copy(previewParameter = previewParameter.copy(index = index)) } public data object Empty : PaparazziPreviewData { @@ -24,8 +49,84 @@ public sealed interface PaparazziPreviewData { public data class Error( val snapshotName: String, + val preview: PreviewData, val message: String ) : PaparazziPreviewData { - override fun toString(): String = snapshotName + override fun toString(): String = buildList { + add(snapshotName) + preview.toString().takeIf { it.isNotEmpty() }?.let(::add) + }.joinToString(",") + } +} + +public data class PreviewData( + val fontScale: Float? = null, + val device: String? = null, + val widthDp: Int? = null, + val heightDp: Int? = null, + val uiMode: Int? = null, + val locale: String? = null, + val backgroundColor: String? = null +) { + override fun toString(): String = buildList { + fontScale?.fontScale()?.displayName()?.let(::add) + uiMode?.lightDarkName()?.let(::add) + uiMode?.uiModeName()?.let(::add) + device?.let { + if (it != PaparazziPreviewDefaults.DEVICE_ID) { + add(it.substringAfterLast(":")) + } + } + widthDp?.let { add("w_$it") } + heightDp?.let { add("h_$it") } + locale?.let(::add) + backgroundColor?.let { add("bg_$it") } + }.takeIf { it.isNotEmpty() } + ?.joinToString(",") + ?: "" +} + +public data class PreviewParameterData( + val name: String, + val values: Sequence, + val index: Int = 0 +) { + override fun toString(): String = "$name$index" +} + +/** + * Maps [fontScale] to enum values similar to Preview + * see: +https://android.googlesource.com/platform/tools/adt/idea/+/refs/heads/mirror-goog-studio-main/compose-designer/src/com/android/tools/idea/compose/pickers/preview/enumsupport/PsiEnumValues.kt + */ +internal fun Float.fontScale() = + FontScale.values().find { this == it.value } ?: FontScale.CUSTOM.apply { value = this@fontScale } + +internal enum class FontScale(var value: Float?) { + DEFAULT(1f), + SMALL(0.85f), + LARGE(1.15f), + LARGEST(1.30f), + CUSTOM(null); + + fun displayName() = when (this) { + CUSTOM -> "fs_$value" + else -> name } } + +internal fun Int.lightDarkName() = when (this and Configuration.UI_MODE_NIGHT_MASK) { + Configuration.UI_MODE_NIGHT_NO -> "Light" + Configuration.UI_MODE_NIGHT_YES -> "Dark" + else -> null +} + +internal fun Int.uiModeName() = when (this and Configuration.UI_MODE_TYPE_MASK) { + Configuration.UI_MODE_TYPE_NORMAL -> "Normal" + Configuration.UI_MODE_TYPE_CAR -> "Car" + Configuration.UI_MODE_TYPE_DESK -> "Desk" + Configuration.UI_MODE_TYPE_APPLIANCE -> "Appliance" + Configuration.UI_MODE_TYPE_WATCH -> "Watch" + Configuration.UI_MODE_TYPE_VR_HEADSET -> "VR_Headset" + else -> null +} diff --git a/paparazzi-gradle-plugin/build.gradle b/paparazzi-gradle-plugin/build.gradle index e5ae3979b9..5ea547b5f3 100644 --- a/paparazzi-gradle-plugin/build.gradle +++ b/paparazzi-gradle-plugin/build.gradle @@ -66,6 +66,7 @@ tasks.withType(Test).configureEach { dependsOn(':paparazzi:publishMavenPublicationToProjectLocalMavenRepository') dependsOn(':paparazzi-annotations:publishMavenPublicationToProjectLocalMavenRepository') dependsOn(':paparazzi-preview-processor:publishMavenPublicationToProjectLocalMavenRepository') + dependsOn(':paparazzi-preview-test:publishMavenPublicationToProjectLocalMavenRepository') } // When cleaning this project, we also want to clean the test projects. diff --git a/paparazzi-gradle-plugin/src/main/java/app/cash/paparazzi/gradle/PaparazziPlugin.kt b/paparazzi-gradle-plugin/src/main/java/app/cash/paparazzi/gradle/PaparazziPlugin.kt index 284e5a8bc0..23a85616fc 100644 --- a/paparazzi-gradle-plugin/src/main/java/app/cash/paparazzi/gradle/PaparazziPlugin.kt +++ b/paparazzi-gradle-plugin/src/main/java/app/cash/paparazzi/gradle/PaparazziPlugin.kt @@ -251,6 +251,7 @@ public class PaparazziPlugin @Inject constructor( project.addAnnotationsDependency() project.addProcessorDependency() + project.addPreviewTestDependency() project.registerGeneratePreviewTask(config, extension) project.afterEvaluate { @@ -347,6 +348,15 @@ public class PaparazziPlugin @Inject constructor( } } + private fun Project.addPreviewTestDependency() { + val dependency = if (isInternal()) { + dependencies.project(mapOf("path" to ":paparazzi-preview-test")) + } else { + dependencies.create("app.cash.paparazzi:paparazzi-preview-test:$VERSION") + } + configurations.getByName("testImplementation").dependencies.add(dependency) + } + private fun Project.isInternal(): Boolean = providers.gradleProperty("app.cash.paparazzi.internal").orNull == "true" diff --git a/paparazzi-gradle-plugin/src/main/java/app/cash/paparazzi/gradle/PreviewTests.kt b/paparazzi-gradle-plugin/src/main/java/app/cash/paparazzi/gradle/PreviewTests.kt index 8c34bfe1cc..0b353e49f5 100644 --- a/paparazzi-gradle-plugin/src/main/java/app/cash/paparazzi/gradle/PreviewTests.kt +++ b/paparazzi-gradle-plugin/src/main/java/app/cash/paparazzi/gradle/PreviewTests.kt @@ -1,9 +1,9 @@ package app.cash.paparazzi.gradle -private const val PREVIEW_TEST_SOURCE = """ +internal const val PREVIEW_TEST_SOURCE = """ import app.cash.paparazzi.Paparazzi +import app.cash.paparazzi.annotations.PaparazziPreviewData import app.cash.paparazzi.preview.DefaultLocaleRule -import app.cash.paparazzi.preview.PaparazziPreviewData import app.cash.paparazzi.preview.PaparazziValuesProvider import app.cash.paparazzi.preview.deviceConfig import app.cash.paparazzi.preview.locale diff --git a/paparazzi-gradle-plugin/src/main/java/app/cash/paparazzi/gradle/utils/PreviewUtils.kt b/paparazzi-gradle-plugin/src/main/java/app/cash/paparazzi/gradle/utils/PreviewUtils.kt index 99de69be0d..8eb860691d 100644 --- a/paparazzi-gradle-plugin/src/main/java/app/cash/paparazzi/gradle/utils/PreviewUtils.kt +++ b/paparazzi-gradle-plugin/src/main/java/app/cash/paparazzi/gradle/utils/PreviewUtils.kt @@ -1,5 +1,6 @@ package app.cash.paparazzi.gradle.utils +import app.cash.paparazzi.gradle.PREVIEW_TEST_SOURCE import app.cash.paparazzi.gradle.PaparazziExtension import com.android.build.api.variant.AndroidComponentsExtension import com.android.build.api.variant.HasUnitTest @@ -27,19 +28,22 @@ internal fun Project.registerGeneratePreviewTask( val taskName = "paparazziGeneratePreview${testVariantSlug}Kotlin" val taskProvider = tasks.register(taskName) { task -> task.group = VERIFICATION_GROUP - task.description = "Generates the preview test class to the test source set for $testVariantSlug" + task.description = + "Generates the preview test class to the test source set for $testVariantSlug" task.dependsOn("ksp${buildTypeCap}Kotlin") } - val testSourceDir = "$projectDir${File.separator}$TEST_SOURCE_DIR${File.separator}${buildType}UnitTest" + val testSourceDir = + "$projectDir${File.separator}$TEST_SOURCE_DIR${File.separator}${buildType}UnitTest" testVariant.sources.java?.addStaticSourceDirectory(testSourceDir) // test compilation depends on the task - project.tasks.named { it == "compile${testVariantSlug}Kotlin" } - .configureEach { it.dependsOn(taskProvider) } - project.tasks.named { it == "generate${testVariantSlug}LintModel" } - .configureEach { it.dependsOn(taskProvider) } + project.tasks.named { + it == "compile${testVariantSlug}Kotlin" || + it == "generate${testVariantSlug}LintModel" || + it == "lintAnalyze$testVariantSlug" + }.configureEach { it.dependsOn(taskProvider) } // run task before processing symbols project.tasks.named { it == "ksp${testVariantSlug}Kotlin" } .configureEach { it.mustRunAfter(taskProvider) } @@ -53,7 +57,9 @@ internal fun Project.registerGeneratePreviewTask( // Optional input if KSP doesn't output preview annotation file task.inputs - .file("$projectDir${File.separator}$KSP_SOURCE_DIR${File.separator}${buildType}${File.separator}kotlin${File.separator}$namespaceDir${File.separator}$PREVIEW_DATA_FILE") + .file( + "$projectDir${File.separator}$KSP_SOURCE_DIR${File.separator}${buildType}${File.separator}kotlin${File.separator}$namespaceDir${File.separator}$PREVIEW_DATA_FILE" + ) .optional() .skipWhenEmpty() task.enabled = config.generatePreviewTestClass.get() @@ -78,36 +84,3 @@ internal fun Project.registerGeneratePreviewTask( private fun String.capitalize() = replaceFirstChar { if (it.isLowerCase()) it.titlecase(Locale.ROOT) else it.toString() } - -private const val PREVIEW_TEST_SOURCE = """ -import app.cash.paparazzi.Paparazzi -import app.cash.paparazzi.annotations.PaparazziPreviewData -import app.cash.paparazzi.preview.PaparazziValuesProvider -import app.cash.paparazzi.preview.snapshot -import com.android.ide.common.rendering.api.SessionParams.RenderingMode.SHRINK -import com.google.testing.junit.testparameterinjector.TestParameter -import com.google.testing.junit.testparameterinjector.TestParameterInjector -import org.junit.Assume.assumeTrue -import org.junit.Rule -import org.junit.Test -import org.junit.runner.RunWith - -@RunWith(TestParameterInjector::class) -class PreviewTests( - @TestParameter(valuesProvider = PreviewConfigValuesProvider::class) - private val preview: PaparazziPreviewData, -) { - private class PreviewConfigValuesProvider : PaparazziValuesProvider(paparazziPreviews) - - @get:Rule - val paparazzi = Paparazzi( - renderingMode = SHRINK, - ) - - @Test - fun preview() { - assumeTrue(preview !is PaparazziPreviewData.Empty) - paparazzi.snapshot(preview) - } -} -""" diff --git a/paparazzi-gradle-plugin/src/test/java/app/cash/paparazzi/gradle/PaparazziPluginTest.kt b/paparazzi-gradle-plugin/src/test/java/app/cash/paparazzi/gradle/PaparazziPluginTest.kt index 9dc1e87e70..953ac3e40f 100644 --- a/paparazzi-gradle-plugin/src/test/java/app/cash/paparazzi/gradle/PaparazziPluginTest.kt +++ b/paparazzi-gradle-plugin/src/test/java/app/cash/paparazzi/gradle/PaparazziPluginTest.kt @@ -1425,20 +1425,7 @@ class PaparazziPluginTest { .runFixture(fixtureRoot) { buildAndFail() } assertThat(result.task(":testDebugUnitTest")?.outcome).isEqualTo(TaskOutcome.FAILED) - assertThat(result.output).contains("java.lang.Exception at PreviewTests.kt:30") - } - - @Test - fun previewAnnotationPreviewParameters() { - val fixtureRoot = File("src/test/projects/preview-annotation-preview-parameters") - - val result = gradleRunner - .withArguments("verifyPaparazziDebug", "--stacktrace") - .forwardOutput() - .runFixture(fixtureRoot) { buildAndFail() } - - assertThat(result.task(":testDebugUnitTest")?.outcome).isEqualTo(TaskOutcome.FAILED) - assertThat(result.output).contains("java.lang.Exception at PreviewTests.kt:30") + assertThat(result.output).contains("java.lang.Exception at PreviewTests.kt") } @Test @@ -1463,7 +1450,7 @@ class PaparazziPluginTest { val result = gradleRunner .forwardOutput() .withArguments("verifyPaparazziDebug", "--stacktrace") - .runFixture(fixtureRoot) { buildAndFail() } // Currently fails because of preview parameter usage + .runFixture(fixtureRoot) { build() } // Currently fails because of preview parameter usage assertThat(result.task(":paparazziGeneratePreviewDebugUnitTestKotlin")).isNotNull() diff --git a/paparazzi-gradle-plugin/src/test/projects/preview-annotation-empty-test-suite/src/test/snapshots/images/app.cash.paparazzi.plugin.test_PreviewTests_preview[HelloPaparazzi_HelloPaparazzi].png b/paparazzi-gradle-plugin/src/test/projects/preview-annotation-empty-test-suite/src/test/snapshots/images/app.cash.paparazzi.plugin.test_PreviewTests_preview[HelloPaparazzi_HelloPaparazzi].png index a76cda1f40d03760dee61743915148750e745603..3edf07abb407510c94cc467c7fda6ba6f519b74e 100644 GIT binary patch literal 4073 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 b4dMR)Oes20gN(2I00000NkvXXu0mjfu*T^7 literal 4514 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 A;{X5v diff --git a/paparazzi-gradle-plugin/src/test/projects/preview-annotation-preview-parameters/build.gradle b/paparazzi-gradle-plugin/src/test/projects/preview-annotation-preview-parameters/build.gradle deleted file mode 100644 index bd67b89749..0000000000 --- a/paparazzi-gradle-plugin/src/test/projects/preview-annotation-preview-parameters/build.gradle +++ /dev/null @@ -1,33 +0,0 @@ -plugins { - id 'com.android.library' - id 'kotlin-android' - id 'app.cash.paparazzi' - id 'org.jetbrains.kotlin.plugin.compose' -} - -paparazzi { - generatePreviewTestClass = true -} - -android { - namespace 'app.cash.paparazzi.plugin.test' - compileSdk libs.versions.compileSdk.get() as int - defaultConfig { - minSdk libs.versions.minSdk.get() as int - } - compileOptions { - sourceCompatibility = libs.versions.javaTarget.get() - targetCompatibility = libs.versions.javaTarget.get() - } - kotlinOptions { - jvmTarget = libs.versions.javaTarget.get() - } - buildFeatures { - compose true - } -} - -dependencies { - implementation libs.composeUi.material - implementation libs.composeUi.uiTooling -} diff --git a/paparazzi-gradle-plugin/src/test/projects/preview-annotation-preview-parameters/src/main/java/app/cash/paparazzi/plugin/test/HelloPaparazzi.kt b/paparazzi-gradle-plugin/src/test/projects/preview-annotation-preview-parameters/src/main/java/app/cash/paparazzi/plugin/test/HelloPaparazzi.kt deleted file mode 100644 index 7f34adc5f2..0000000000 --- a/paparazzi-gradle-plugin/src/test/projects/preview-annotation-preview-parameters/src/main/java/app/cash/paparazzi/plugin/test/HelloPaparazzi.kt +++ /dev/null @@ -1,22 +0,0 @@ -package app.cash.paparazzi.plugin.test - -import androidx.compose.material.Text -import androidx.compose.runtime.Composable -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.tooling.preview.PreviewParameter -import androidx.compose.ui.tooling.preview.PreviewParameterProvider -import app.cash.paparazzi.annotations.Paparazzi - -@Paparazzi -@Preview -@Composable -fun HelloPaparazzi( - @PreviewParameter(NameProvider::class) name: String -) { - Text(text = name) -} - -class NameProvider : PreviewParameterProvider { - override val values: Sequence - get() = sequenceOf("Papa", "Razzi") -} diff --git a/paparazzi-gradle-plugin/src/test/projects/preview-annotation-sample-configuration-cache/src/main/java/app/cash/paparazzi/plugin/test/HelloPaparazzi.kt b/paparazzi-gradle-plugin/src/test/projects/preview-annotation-sample-configuration-cache/src/main/java/app/cash/paparazzi/plugin/test/HelloPaparazzi.kt index 8db06bffdc..1c3909eccc 100644 --- a/paparazzi-gradle-plugin/src/test/projects/preview-annotation-sample-configuration-cache/src/main/java/app/cash/paparazzi/plugin/test/HelloPaparazzi.kt +++ b/paparazzi-gradle-plugin/src/test/projects/preview-annotation-sample-configuration-cache/src/main/java/app/cash/paparazzi/plugin/test/HelloPaparazzi.kt @@ -1,12 +1,8 @@ package app.cash.paparazzi.plugin.test -import android.content.res.Configuration import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.tooling.preview.PreviewParameter -import androidx.compose.ui.tooling.preview.PreviewParameterProvider -import androidx.compose.ui.tooling.preview.Wallpapers import app.cash.paparazzi.annotations.Paparazzi @Paparazzi @@ -15,36 +11,3 @@ import app.cash.paparazzi.annotations.Paparazzi fun HelloPaparazzi() { Text("Hello, Paparazzi!") } - -@Paparazzi -@Preview -@Composable -fun HelloPaparazziParameterized( - @PreviewParameter(provider = PreviewData::class) text: String, -) { - Text(text) -} - -@Paparazzi -@Preview( - name = "PreviewConfig", - group = "Previews", - device = "id:Nexus 6", - apiLevel = 33, - showSystemUi = true, - uiMode = Configuration.UI_MODE_NIGHT_YES or Configuration.UI_MODE_TYPE_NORMAL, - wallpaper = Wallpapers.RED_DOMINATED_EXAMPLE, - showBackground = true, - fontScale = 1.5f -) -@Composable -fun HelloPaparazziPreviewConfig() { - Text("Hello Paparazzi Preview Config!") -} - -object PreviewData : PreviewParameterProvider { - override val values: Sequence = sequenceOf( - "Hello, Paparazzi One!", - "Hello, Paparazzi Two!", - ) -} diff --git a/paparazzi-gradle-plugin/src/test/projects/preview-annotation-sample/src/test/snapshots/images/app.cash.paparazzi.plugin.test_PreviewTests_preview[HelloPaparazzi_HelloPaparazziParameterized,text0].png b/paparazzi-gradle-plugin/src/test/projects/preview-annotation-sample/src/test/snapshots/images/app.cash.paparazzi.plugin.test_PreviewTests_preview[HelloPaparazzi_HelloPaparazziParameterized,text0].png new file mode 100644 index 0000000000000000000000000000000000000000..9b577c683c0fd1adc01c9094985f5b3961ef0342 GIT binary patch literal 2995 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 }hcQLG59Kz2jt`xb(=fwgl>2HkrLD zbnfck+A|Oyj7Z~v5DeqQZh$U&AGJ+$N~umHe)BzE$_`*=GwYR(zK>cH&r@D3jZVX| zGmG7J5Hes(=l<6YYV6jDP}>4;fOpG5XR0o9piW*6Wl|@IzuSCc+B{d69(J~ZdbABT zH#e;?Y2#X@mCDGnwgzgOURTP6o~!%X)4QV?VaGU!sMJ$m{1;w$G0g&*5*sT#gfkOY0K)ibWSveb(aHmo*z&FRugzxct6QR$@Fc-%y{k6i!=FR5i0T_HOa zqgq=#@UDozx26+KrRR+(e`GmJ*Hviy+LsaP=;opZM^VYXvcE9Vd;8he)O+A16bk(g zP#;>F-9iZGw0K(q>QE>^y$P|5(b!ebuX_ZjL!nTB+EK*U(}}5`jfwzuC=?1%A83=I z<%015btn`HP=^B4p-_N26#74>mmK4y0Cgx7pbmvXp?_BY0T;yeavwsTE&u=k07*qo IM6N<$g5UVMssI20 literal 0 HcmV?d00001 diff --git a/paparazzi-gradle-plugin/src/test/projects/preview-annotation-sample/src/test/snapshots/images/app.cash.paparazzi.plugin.test_PreviewTests_preview[HelloPaparazzi_HelloPaparazziPreviewConfig,fs_1.5,Dark,Normal,Nexus 6].png b/paparazzi-gradle-plugin/src/test/projects/preview-annotation-sample/src/test/snapshots/images/app.cash.paparazzi.plugin.test_PreviewTests_preview[HelloPaparazzi_HelloPaparazziPreviewConfig,fs_1.5,Dark,Normal,Nexus 6].png new file mode 100644 index 0000000000000000000000000000000000000000..08449fa476f6728d9789066edad436e1dc0e7217 GIT binary patch literal 4787 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 literal 0 HcmV?d00001 diff --git a/paparazzi-gradle-plugin/src/test/projects/preview-annotation-sample/src/test/snapshots/images/app.cash.paparazzi.plugin.test_PreviewTests_preview[HelloPaparazzi_HelloPaparazziPreviewConfig].png b/paparazzi-gradle-plugin/src/test/projects/preview-annotation-sample/src/test/snapshots/images/app.cash.paparazzi.plugin.test_PreviewTests_preview[HelloPaparazzi_HelloPaparazziPreviewConfig].png deleted file mode 100644 index 828d6d4c56bf037919b6ceadc685b7d9d82d121e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4583 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 diff --git a/paparazzi-gradle-plugin/src/test/projects/preview-annotation-sample/src/test/snapshots/images/app.cash.paparazzi.plugin.test_PreviewTests_preview[HelloPaparazzi_HelloPaparazzi].png b/paparazzi-gradle-plugin/src/test/projects/preview-annotation-sample/src/test/snapshots/images/app.cash.paparazzi.plugin.test_PreviewTests_preview[HelloPaparazzi_HelloPaparazzi].png index d838caf940a54b48f6f2091488d866ddd9b3a36e..da18192acb268e3dadb8f70882e07878565b85d7 100644 GIT binary patch literal 2410 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 - val visibilityCheck = checkVisibility(func) + functions.process { func, preview, previewParam -> + val visibilityCheck = checkVisibility(func, previewParam) val snapshotName = func.snapshotName(env) when { @@ -53,21 +55,19 @@ internal object PaparazziPoet { visibilityCheck = visibilityCheck, function = func, snapshotName = snapshotName, - buildErrorMessage = { - "$it is private. Make it internal or public to generate a snapshot." - } + preview = preview, + previewParam = previewParam ) - previewParam != null -> addError( - visibilityCheck = visibilityCheck, + previewParam != null -> addProvider( function = func, snapshotName = snapshotName, - buildErrorMessage = { - "$it preview uses PreviewParameters which aren't currently supported." - } + preview = preview, + previewParam = previewParam ) else -> addDefault( function = func, - snapshotName = snapshotName + snapshotName = snapshotName, + preview = preview ) } } @@ -84,32 +84,56 @@ internal object PaparazziPoet { } private fun Sequence.process( - block: (KSFunctionDeclaration, KSValueParameter?) -> Unit + block: (KSFunctionDeclaration, PreviewModel, KSValueParameter?) -> Unit ) = flatMap { func -> val previewParam = func.previewParam() func.findDistinctPreviews() - .map { Pair(func, previewParam) } - }.forEach { (func, previewParam) -> - block(func, previewParam) + .map { AcceptableAnnotationsProcessData(func, it, previewParam) } + }.forEach { (func, preview, previewParam) -> + block(func, preview, previewParam) } + private data class AcceptableAnnotationsProcessData( + val func: KSFunctionDeclaration, + val model: PreviewModel, + val previewParam: KSValueParameter? + ) + private fun CodeBlock.Builder.addError( visibilityCheck: VisibilityCheck, function: KSFunctionDeclaration, snapshotName: String, - buildErrorMessage: (String?) -> String + preview: PreviewModel, + previewParam: KSValueParameter? ) { val qualifiedName = if (visibilityCheck.isFunctionPrivate) { function.qualifiedName?.asString() } else { - null + previewParam?.previewParamProvider()?.qualifiedName?.asString() } addStatement("%L.PaparazziPreviewData.Error(", PACKAGE_NAME) indent() addStatement("snapshotName = %S,", snapshotName) - addStatement("message = %S,", buildErrorMessage(qualifiedName)) + addStatement("message = %S,", "$qualifiedName is private. Make it internal or public to generate a snapshot.") + addPreviewData(preview) + unindent() + addStatement("),") + } + + private fun CodeBlock.Builder.addProvider( + function: KSFunctionDeclaration, + snapshotName: String, + preview: PreviewModel, + previewParam: KSValueParameter + ) { + addStatement("%L.PaparazziPreviewData.Provider(", PACKAGE_NAME) + indent() + addStatement("snapshotName = %S,", snapshotName) + addStatement("composable = { %L(it) },", function.qualifiedName?.asString()) + addPreviewParameterData(previewParam) + addPreviewData(preview) unindent() addStatement("),") } @@ -128,12 +152,55 @@ internal object PaparazziPoet { private fun CodeBlock.Builder.addDefault( function: KSFunctionDeclaration, - snapshotName: String + snapshotName: String, + preview: PreviewModel ) { addStatement("%L.PaparazziPreviewData.Default(", PACKAGE_NAME) indent() addStatement("snapshotName = %S,", snapshotName) addStatement("composable = { %L() },", function.qualifiedName?.asString()) + addPreviewData(preview) + unindent() + addStatement("),") + } + + private fun CodeBlock.Builder.addPreviewData(preview: PreviewModel) { + addStatement("preview = %L.PreviewData(", PACKAGE_NAME) + indent() + + preview.fontScale.takeIf { it != 1f } + ?.let { addStatement("fontScale = %Lf,", it) } + + preview.device.takeIf { it.isNotEmpty() } + ?.let { addStatement("device = %S,", it) } + + preview.widthDp.takeIf { it > -1 } + ?.let { addStatement("widthDp = %L,", it) } + + preview.heightDp.takeIf { it > -1 } + ?.let { addStatement("heightDp = %L,", it) } + + preview.uiMode.takeIf { it != 0 } + ?.let { addStatement("uiMode = %L,", it) } + + preview.locale.takeIf { it.isNotEmpty() } + ?.let { addStatement("locale = %S,", it) } + + preview.backgroundColor.takeIf { it != 0L && preview.showBackground } + ?.let { addStatement("backgroundColor = %S", it.toString(16)) } + + unindent() + addStatement("),") + } + + private fun CodeBlock.Builder.addPreviewParameterData(previewParam: KSValueParameter) { + addStatement("previewParameter = %L.PreviewParameterData(", PACKAGE_NAME) + indent() + addStatement("name = %S,", previewParam.name?.asString()) + val previewParamProvider = previewParam.previewParamProvider() + val isClassObject = previewParamProvider.closestClassDeclaration()?.classKind == ClassKind.OBJECT + val previewParamProviderInstantiation = "${previewParamProvider.qualifiedName?.asString()}${if (isClassObject) "" else "()"}" + addStatement("values = %L.values,", previewParamProviderInstantiation) unindent() addStatement("),") } @@ -149,14 +216,17 @@ internal object PaparazziPoet { }.joinToString("_") private fun checkVisibility( - function: KSFunctionDeclaration + function: KSFunctionDeclaration, + previewParam: KSValueParameter? ) = VisibilityCheck( - isFunctionPrivate = function.getVisibility() == Visibility.PRIVATE + isFunctionPrivate = function.getVisibility() == Visibility.PRIVATE, + isPreviewParamProviderPrivate = previewParam?.previewParamProvider()?.getVisibility() == Visibility.PRIVATE ) } internal data class VisibilityCheck( - val isFunctionPrivate: Boolean + val isFunctionPrivate: Boolean, + val isPreviewParamProviderPrivate: Boolean ) { val isPrivate = isFunctionPrivate } diff --git a/paparazzi-preview-processor/src/main/java/app/cash/paparazzi/preview/processor/Utils.kt b/paparazzi-preview-processor/src/main/java/app/cash/paparazzi/preview/processor/Utils.kt index 0deb57adcd..e6f0559da9 100644 --- a/paparazzi-preview-processor/src/main/java/app/cash/paparazzi/preview/processor/Utils.kt +++ b/paparazzi-preview-processor/src/main/java/app/cash/paparazzi/preview/processor/Utils.kt @@ -4,6 +4,8 @@ import com.google.devtools.ksp.symbol.FunctionKind.TOP_LEVEL import com.google.devtools.ksp.symbol.KSAnnotated import com.google.devtools.ksp.symbol.KSAnnotation import com.google.devtools.ksp.symbol.KSFunctionDeclaration +import com.google.devtools.ksp.symbol.KSType +import com.google.devtools.ksp.symbol.KSValueParameter internal const val PACKAGE_NAME = "app.cash.paparazzi.annotations" @@ -14,6 +16,11 @@ internal fun KSAnnotation.isPreviewParameter() = qualifiedName() == "androidx.co internal fun KSAnnotation.qualifiedName() = declaration().qualifiedName?.asString() ?: "" internal fun KSAnnotation.declaration() = annotationType.resolve().declaration +@Suppress("UNCHECKED_CAST") +public fun KSAnnotation.previewArg(name: String): T = arguments + .first { it.name?.asString() == name } + .let { it.value as T } + internal fun Sequence.findPaparazzi() = filterIsInstance() .filter { @@ -35,12 +42,43 @@ internal fun Sequence.findPreviews(stack: Set = setO return direct.plus(indirect) } -internal fun KSFunctionDeclaration.findDistinctPreviews() = annotations.findPreviews().distinct() +internal fun KSFunctionDeclaration.findDistinctPreviews() = annotations.findPreviews().toList() + .map { preview -> + PreviewModel( + fontScale = preview.previewArg("fontScale"), + device = preview.previewArg("device"), + widthDp = preview.previewArg("widthDp"), + heightDp = preview.previewArg("heightDp"), + uiMode = preview.previewArg("uiMode"), + locale = preview.previewArg("locale"), + backgroundColor = preview.previewArg("backgroundColor"), + showBackground = preview.previewArg("showBackground") + ) + } + .distinct() internal fun KSFunctionDeclaration.previewParam() = parameters.firstOrNull { param -> param.annotations.any { it.isPreviewParameter() } } +internal fun KSValueParameter.previewParamProvider() = annotations + .first { it.isPreviewParameter() } + .arguments + .first { arg -> arg.name?.asString() == "provider" } + .let { it.value as KSType } + .declaration + +internal data class PreviewModel( + val fontScale: Float, + val device: String, + val widthDp: Int, + val heightDp: Int, + val uiMode: Int, + val locale: String, + val backgroundColor: Long, + val showBackground: Boolean +) + internal data class EnvironmentOptions( val namespace: String ) diff --git a/sample/src/main/java/app/cash/paparazzi/sample/HelloPaparazzi.kt b/sample/src/main/java/app/cash/paparazzi/sample/HelloPaparazzi.kt index c140df2349..8187d3974a 100644 --- a/sample/src/main/java/app/cash/paparazzi/sample/HelloPaparazzi.kt +++ b/sample/src/main/java/app/cash/paparazzi/sample/HelloPaparazzi.kt @@ -1,5 +1,6 @@ package app.cash.paparazzi.sample +import android.content.res.Configuration import androidx.compose.foundation.background import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize @@ -13,6 +14,8 @@ import androidx.compose.ui.text.font.FontFamily import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextDecoration import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.tooling.preview.PreviewParameter +import androidx.compose.ui.tooling.preview.PreviewParameterProvider import app.cash.paparazzi.annotations.Paparazzi @Paparazzi @@ -50,3 +53,45 @@ fun HelloPaparazzi() { ) } } + +@Paparazzi +@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES) +@Preview +@Composable +fun HelloPaparazzi( + @PreviewParameter(TextProvider::class) text: String +) { + Column( + Modifier + .background(Color.White) + .wrapContentSize() + ) { + Text(text) + Text(text, style = TextStyle(fontFamily = FontFamily.Cursive)) + Text( + text = text, + style = TextStyle(textDecoration = TextDecoration.LineThrough) + ) + Text( + text = text, + style = TextStyle(textDecoration = TextDecoration.Underline) + ) + Text( + text = text, + style = TextStyle( + textDecoration = TextDecoration.combine( + listOf( + TextDecoration.Underline, + TextDecoration.LineThrough + ) + ), + fontWeight = FontWeight.Bold + ) + ) + } +} + +object TextProvider : PreviewParameterProvider { + override val values: Sequence = + sequenceOf("Hello Paparazzi", "Hello World", "Hello Compose") +}