Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implemented Showkase + Paparazzi artifact to automate screenshot testing #294

Merged
merged 19 commits into from Jan 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 4 additions & 2 deletions build.gradle
Expand Up @@ -13,7 +13,7 @@ buildscript {
'detekt' : '1.7.4',
'espresso' : '3.2.0',
'gradle' : '7.2.1',
'junit' : '4.13',
'junit' : '4.13.2',
'junitImplementation' : '1.1.2',
'kotlin' : '1.7.10',
'kotlinCompilerVersion' : '1.7.0',
Expand All @@ -23,6 +23,7 @@ buildscript {
'ksp' : "$KSP_VERSION",
'ktx' : '1.1.0',
'lifecycle' : '2.2.0',
'paparazzi' : '1.1.0',
'picasso' : '2.8',
'appcompat' : '1.4.0',
'testRunner' : '1.4.0',
Expand Down Expand Up @@ -77,7 +78,8 @@ buildscript {
'androidxTestRunner' : "androidx.test:runner:${versions.testRunner}",
'strikt' : "io.strikt:strikt-core:${versions.strikt}",
'shotAndroid' : "com.karumi:shot-android:${versions.shot}",
'testParameterInjector': "com.google.testparameterinjector:test-parameter-injector:${versions.testParameterInjector}"
'testParameterInjector': "com.google.testparameterinjector:test-parameter-injector:${versions.testParameterInjector}",
'paparazzi' : "app.cash.paparazzi:paparazzi:${versions.paparazzi}"
],
'material' : [
'material' : "com.google.android.material:material:${versions.material}",
Expand Down
1 change: 1 addition & 0 deletions settings.gradle
Expand Up @@ -10,3 +10,4 @@ include ':showkase-browser-testing'
include ':showkase-browser-testing-submodule'
include ':showkase-screenshot-testing-shot'
include ':showkase-screenshot-testing-paparazzi-sample'
include ':showkase-screenshot-testing-paparazzi'
2 changes: 2 additions & 0 deletions showkase-processor-testing/build.gradle
Expand Up @@ -83,6 +83,8 @@ dependencies {
testImplementation deps.test.junit
testImplementation deps.kotlinCompileTesting
testImplementation deps.kotlinCompileTestingKsp
testImplementation project(':showkase-screenshot-testing-paparazzi')
testImplementation deps.test.paparazzi
}

// Needed for Java17 otherwise these tests failed to run locally.
Expand Down
Expand Up @@ -16,7 +16,7 @@ import java.io.File
* Temporarily set this to true to have the test runner update test resource file expected outputs
* instead of failing tests on mismatch. Use this to easily update expected outputs.
*/
const val UPDATE_TEST_OUTPUTS = false
const val UPDATE_TEST_OUTPUTS = true
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not 100% sure about this but should we set this to false in the master branch? Thinking since now, it will generate the test output always and not verify that the output is actually correct 🤔
Wdyt? @vinaygaba :)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Made #300 to address this :)


abstract class BaseProcessorTest {
@Rule
Expand Down
Expand Up @@ -202,7 +202,7 @@ class ShowkaseProcessorTest : BaseProcessorTest() {
@Test
fun `open class with no interface but ShowkaseScreenshoTest annotation throws compilation error`() {
assertCompilationFails(
"Only an implementation of com.airbnb.android.showkase.screenshot.testing.ShowkaseScreenshotTest can be annotated with @ShowkaseScreenshot"
"Only an implementation of com.airbnb.android.showkase.screenshot.testing.ShowkaseScreenshotTest or com.airbnb.android.showkase.screenshot.testing.paparazzi.PaparazziShowkaseScreenshotTest can be annotated with @ShowkaseScreenshot"
)
}

Expand All @@ -211,6 +211,21 @@ class ShowkaseProcessorTest : BaseProcessorTest() {
assertCompilationFails("Class annotated with ShowkaseScreenshot needs to be an abstract/open class")
}

@Test
fun `closed class with PaparazziShowkaseScreenshotTest and ShowkaseScreensho annotation throws compilation error`() {
assertCompilationFails("Class annotated with ShowkaseScreenshot needs to be an abstract/open class")
}

@Test
fun `class implementing PaparazziShowkaseScreenshotTest but not companion object throws compilation error`() {
assertCompilationFails("Classes implementing the com.airbnb.android.showkase.screenshot.testing.paparazzi.PaparazziShowkaseScreenshotTest interface should have a companion object that implements the com.airbnb.android.showkase.screenshot.testing.paparazzi.PaparazziShowkaseScreenshotTest.CompanionObject interface")
}

@Test
fun `class implementing PaparazziShowkaseScreenshotTest and companion object implementing different interface throws compilation error`() {
assertCompilationFails("Classes implementing the com.airbnb.android.showkase.screenshot.testing.paparazzi.PaparazziShowkaseScreenshotTest interface should have a companion object that implements the com.airbnb.android.showkase.screenshot.testing.paparazzi.PaparazziShowkaseScreenshotTest.CompanionObject interface")
}

@Test
fun `top level composable function with showkase annotation generates only metadata file`() {
compileInputsAndVerifyOutputs()
Expand Down Expand Up @@ -508,11 +523,21 @@ class ShowkaseProcessorTest : BaseProcessorTest() {
compileInputsAndVerifyOutputs()
}

@Test
fun `top level composable and class with @ScreenshotTest generates Paparazzi screenshot test for composable`() {
compileInputsAndVerifyOutputs()
}

@Test
fun `top level color and class with @ScreenshotTest generates screenshot test for composable`() {
compileInputsAndVerifyOutputs()
}

@Test
fun `top level color and class with @ScreenshotTest generates paparazzi screenshot test for composable`() {
compileInputsAndVerifyOutputs()
}

@Test
fun `top level textstyle and class with @ScreenshotTest generates screenshot test for composable`() {
compileInputsAndVerifyOutputs()
Expand All @@ -528,6 +553,11 @@ class ShowkaseProcessorTest : BaseProcessorTest() {
compileInputsAndVerifyOutputs()
}

@Test
fun `class with @ScreenshotTest generates paparazzi screenshot test for all UI elements`() {
compileInputsAndVerifyOutputs()
}

@Test
fun `composable function with multiple preview functions compiles`() {
compileInputsAndVerifyOutputs()
Expand Down
@@ -0,0 +1,8 @@

import android.graphics.Bitmap
import com.airbnb.android.showkase.annotation.ShowkaseScreenshot
import com.airbnb.android.showkase.screenshot.testing.paparazzi.PaparazziShowkaseScreenshotTest

@ShowkaseScreenshot(rootShowkaseClass = TestShowkaseRoot::class)
public abstract class MyScreenshotTest: PaparazziShowkaseScreenshotTest {
}
@@ -0,0 +1,8 @@

import com.airbnb.android.showkase.annotation.ShowkaseRoot
import com.airbnb.android.showkase.annotation.ShowkaseRootModule

@ShowkaseRoot
public class TestShowkaseRoot: ShowkaseRootModule {

}
@@ -0,0 +1,28 @@

import androidx.compose.runtime.Composable
import com.airbnb.android.showkase.annotation.ShowkaseColor
import com.airbnb.android.showkase.annotation.ShowkaseComposable
import com.airbnb.android.showkase.annotation.ShowkaseTypography
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontFamily

@ShowkaseComposable(name = "name1", group = "group1")
@Composable
public fun TestComposable1() {

}

@ShowkaseComposable(name = "name2", group = "group2")
@Composable
public fun TestComposable2() {

}

@ShowkaseColor("name", "color")
public val red: Color = Color(0xffff0000)

@ShowkaseTypography("name", "typography")
public val title: TextStyle = TextStyle(
fontFamily = FontFamily.Cursive
)
@@ -0,0 +1,7 @@
import android.graphics.Bitmap
import com.airbnb.android.showkase.annotation.ShowkaseScreenshot
import com.airbnb.android.showkase.screenshot.testing.paparazzi.PaparazziShowkaseScreenshotTest

@ShowkaseScreenshot(rootShowkaseClass = TestShowkaseRoot::class)
public abstract class MyScreenshotTest: PaparazziShowkaseScreenshotTest {
}
@@ -0,0 +1,9 @@
import com.airbnb.android.showkase.annotation.ShowkaseComposable
import androidx.compose.runtime.Composable
import com.airbnb.android.showkase.annotation.ShowkaseRoot
import com.airbnb.android.showkase.annotation.ShowkaseRootModule

@ShowkaseRoot
public class TestShowkaseRoot: ShowkaseRootModule {

}
@@ -0,0 +1,27 @@
import androidx.compose.runtime.Composable
import com.airbnb.android.showkase.annotation.ShowkaseColor
import com.airbnb.android.showkase.annotation.ShowkaseComposable
import com.airbnb.android.showkase.annotation.ShowkaseTypography
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontFamily

@ShowkaseComposable(name = "name1", group = "group1")
@Composable
public fun TestComposable1() {

}

@ShowkaseComposable(name = "name2", group = "group2")
@Composable
public fun TestComposable2() {

}

@ShowkaseColor("name", "color")
public val red: Color = Color(0xffff0000)

@ShowkaseTypography("name", "typography")
public val title: TextStyle = TextStyle(
fontFamily = FontFamily.Cursive
)
@@ -0,0 +1,11 @@

import android.graphics.Bitmap
import com.airbnb.android.showkase.annotation.ShowkaseScreenshot
import com.airbnb.android.showkase.screenshot.testing.ShowkaseScreenshotTest
import com.airbnb.android.showkase.screenshot.testing.ShowkaseScreenshotType
import com.airbnb.android.showkase.screenshot.testing.paparazzi.PaparazziShowkaseScreenshotTest

@ShowkaseScreenshot(rootShowkaseClass = TestShowkaseRoot::class)
public abstract class MyScreenshotTest: PaparazziShowkaseScreenshotTest {
public companion object: PaparazziShowkaseScreenshotTest.CompanionObject
}
@@ -0,0 +1,10 @@

import com.airbnb.android.showkase.annotation.ShowkaseComposable
import androidx.compose.runtime.Composable
import com.airbnb.android.showkase.annotation.ShowkaseRoot
import com.airbnb.android.showkase.annotation.ShowkaseRootModule

@ShowkaseRoot
public class TestShowkaseRoot: ShowkaseRootModule {

}
@@ -0,0 +1,28 @@

import androidx.compose.runtime.Composable
import com.airbnb.android.showkase.annotation.ShowkaseColor
import com.airbnb.android.showkase.annotation.ShowkaseComposable
import com.airbnb.android.showkase.annotation.ShowkaseTypography
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontFamily

@ShowkaseComposable(name = "name1", group = "group1")
@Composable
public fun TestComposable1() {

}

@ShowkaseComposable(name = "name2", group = "group2")
@Composable
public fun TestComposable2() {

}

@ShowkaseColor("name", "color")
public val red: Color = Color(0xffff0000)

@ShowkaseTypography("name", "typography")
public val title: TextStyle = TextStyle(
fontFamily = FontFamily.Cursive
)
@@ -0,0 +1,63 @@
// This is an auto-generated file. Please do not edit/modify this file.
import androidx.compose.ui.unit.LayoutDirection
import app.cash.paparazzi.Paparazzi
import com.airbnb.android.showkase.models.Showkase
import com.airbnb.android.showkase.screenshot.testing.paparazzi.ColorPaparazziShowkaseTestPreview
import com.airbnb.android.showkase.screenshot.testing.paparazzi.ComponentPaparazziShowkaseTestPreview
import com.airbnb.android.showkase.screenshot.testing.paparazzi.PaparazziShowkaseDeviceConfig
import com.airbnb.android.showkase.screenshot.testing.paparazzi.PaparazziShowkaseTestPreview
import com.airbnb.android.showkase.screenshot.testing.paparazzi.PaparazziShowkaseUIMode
import com.airbnb.android.showkase.screenshot.testing.paparazzi.TypographyPaparazziShowkaseTestPreview
import com.google.testing.junit.testparameterinjector.TestParameter
import com.google.testing.junit.testparameterinjector.TestParameter.TestParameterValuesProvider
import com.google.testing.junit.testparameterinjector.TestParameterInjector
import getMetadata
import kotlin.Unit
import kotlin.collections.List
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith

@RunWith(TestParameterInjector::class)
public class MyScreenshotTest_PaparazziShowkaseTest : MyScreenshotTest() {
@get:Rule
public val paparazzi: Paparazzi = providePaparazzi()

@Test
public fun test_previews(
@TestParameter(valuesProvider = PaparazziShowkasePreviewProvider::class)
elementPreview: PaparazziShowkaseTestPreview,
@TestParameter(valuesProvider = PaparazziShowkaseDeviceConfigProvider::class)
config: PaparazziShowkaseDeviceConfig,
@TestParameter(valuesProvider = PaparazziShowkaseLayoutDirectionProvider::class)
direction: LayoutDirection,
@TestParameter(valuesProvider = PaparazziShowkaseUIModeProvider::class)
uiMode: PaparazziShowkaseUIMode,
): Unit {
paparazzi.unsafeUpdateConfig(config.deviceConfig.copy(softButtons = false))
takePaparazziSnapshot(paparazzi, elementPreview, direction, uiMode)
}

private object PaparazziShowkasePreviewProvider : TestParameter.TestParameterValuesProvider {
public override fun provideValues(): List<PaparazziShowkaseTestPreview> {
val metadata = Showkase.getMetadata()
val components = metadata.componentList.map(::ComponentPaparazziShowkaseTestPreview)
val colors = metadata.colorList.map(::ColorPaparazziShowkaseTestPreview)
val typography = metadata.typographyList.map(::TypographyPaparazziShowkaseTestPreview)
return components + colors + typography
}
}

private object PaparazziShowkaseDeviceConfigProvider : TestParameter.TestParameterValuesProvider {
public override fun provideValues(): List<PaparazziShowkaseDeviceConfig> = deviceConfigs()
}

private object PaparazziShowkaseLayoutDirectionProvider :
TestParameter.TestParameterValuesProvider {
public override fun provideValues(): List<LayoutDirection> = layoutDirections()
}

private object PaparazziShowkaseUIModeProvider : TestParameter.TestParameterValuesProvider {
public override fun provideValues(): List<PaparazziShowkaseUIMode> = uiModes()
}
}