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

[Paywalls V2] Adds image background tests #1967

Merged
merged 45 commits into from
Dec 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
7f32542
Adds Result to revenuecatui.
JayShortway Dec 5, 2024
a584cf8
Makes a few things fake-public in purchases.
JayShortway Dec 5, 2024
86813c9
Adds some extensions.
JayShortway Dec 5, 2024
6e1badd
Modifies some things to use the new Result class.
JayShortway Dec 5, 2024
04cd4d2
Fixes tests.
JayShortway Dec 5, 2024
0167612
Adds PackageContext and VariableContextTests
JayShortway Dec 5, 2024
1c4d4c4
Adds PresentedStackPartial.
JayShortway Dec 5, 2024
8427cf7
Adds PaywallComponent.toComponentStyle() as well as ways to recursive…
JayShortway Dec 5, 2024
72cdb32
Adds StyleFactory.
JayShortway Dec 5, 2024
52419ea
Adds NonEmptyList.
JayShortway Dec 5, 2024
aa01767
Adds ResultTests.
JayShortway Dec 5, 2024
6359f23
Adds StyleFactoryTests.
JayShortway Dec 6, 2024
97198e2
Merge branch 'main' into pw2-stylefactory
JayShortway Dec 6, 2024
40fe1de
Removes a FIXME.
JayShortway Dec 6, 2024
52c152c
Fixes a typo.
JayShortway Dec 6, 2024
83450c0
Fixes tests, locally at least.
JayShortway Dec 6, 2024
4da75a1
Adds a failing test.
JayShortway Dec 6, 2024
6a8424c
Fixes the failing test.
JayShortway Dec 6, 2024
2fcff8d
Adds a missing @JvmSynthetic.
JayShortway Dec 6, 2024
ba1c466
Adds another failing test.
JayShortway Dec 6, 2024
83561b7
Adds captureToImageCompat and assertPixelColorEquals.
JayShortway Dec 9, 2024
0e5a50b
Adds assertTextColorEquals and assertTextLayoutResult.
JayShortway Dec 9, 2024
884c163
Updates TextComponentViewTests.
JayShortway Dec 9, 2024
5258dec
Minor adjustment to themeChangingTest.
JayShortway Dec 9, 2024
af5f7d5
Some more adjustment to themeChangingTest.
JayShortway Dec 9, 2024
649cc5b
Separates themeChangingTest to a separate file.
JayShortway Dec 9, 2024
a32b3b4
ThemeController now has separate setLight and setDark methods.
JayShortway Dec 9, 2024
7967af5
Updates TextComponentStyle and TextComponentView.
JayShortway Dec 9, 2024
11d5abe
Removes some comments.
JayShortway Dec 9, 2024
5c0423e
Updates assertPixelColorEquals.
JayShortway Dec 9, 2024
ab54f60
Adds BackgroundTests and fixes BackgroundStyle.
JayShortway Dec 9, 2024
1370842
Adjusts a comment.
JayShortway Dec 9, 2024
ce73071
Adjusts another comment.
JayShortway Dec 9, 2024
3b460fd
Adjusts another comment.
JayShortway Dec 9, 2024
61cafa6
Merge branch 'pw2-text-theme-tests' into pw2-background-tests
JayShortway Dec 10, 2024
69f5341
Minor update.
JayShortway Dec 10, 2024
79107d1
Marks Shape.kt as JvmSynthetic at the file level.
JayShortway Dec 10, 2024
e633fa3
Adds `variable` and `locale` as `remember` keys in `rememberProcessed…
JayShortway Dec 10, 2024
87e15f7
Fixes StyleFactoryTests.
JayShortway Dec 10, 2024
dd57c1f
Merge branch 'main' into pw2-stylefactory
JayShortway Dec 10, 2024
842c556
Merge branch 'pw2-stylefactory' into pw2-text-theme-tests
JayShortway Dec 10, 2024
722cac4
Merge branch 'pw2-text-theme-tests' into pw2-background-tests
JayShortway Dec 10, 2024
729e23c
Merge branch 'main' into pw2-text-theme-tests
JayShortway Dec 10, 2024
40e5f11
Merge branch 'pw2-text-theme-tests' into pw2-background-tests
JayShortway Dec 10, 2024
29da06a
Merge branch 'main' into pw2-background-tests
JayShortway Dec 10, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ window-core = { module = "androidx.window:window-core", version.ref = "window" }

coil-compose = { module = "io.coil-kt:coil-compose", version.ref = "coil" }
coil-svg = { module = "io.coil-kt:coil-svg", version.ref = "coil" }
coil-test = { module = "io.coil-kt:coil-test", version.ref = "coil" }

commonmark = { module = "org.commonmark:commonmark", version.ref = "commonmark" }
commonmark-strikethrough = { module = "org.commonmark:commonmark-ext-gfm-strikethrough", version.ref = "commonmark" }
Expand Down
1 change: 1 addition & 0 deletions ui/revenuecatui/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ dependencies {
debugImplementation libs.androidx.test.compose.manifest

testImplementation libs.bundles.test
testImplementation libs.coil.test
testImplementation libs.coroutines.test
testImplementation libs.kotlinx.serialization.json
testImplementation libs.androidx.test.compose
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,9 @@ internal fun Background.toBackgroundStyle(): BackgroundStyle =
val imageUrls = value.urlsForCurrentTheme
BackgroundStyle.Image(
painter = rememberAsyncImagePainter(
model = imageUrls.webp,
model = imageUrls.webp.toString(),
placeholder = rememberAsyncImagePainter(
model = imageUrls.webpLowRes,
model = imageUrls.webpLowRes.toString(),
Comment on lines -40 to +42
Copy link
Member Author

@JayShortway JayShortway Dec 10, 2024

Choose a reason for hiding this comment

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

This bug was caught by the tests. It caused the image to not load at all, as we don't have a Fetcher that supports the URL type.

error = null,
fallback = null,
contentScale = ContentScale.Crop,
Expand Down Expand Up @@ -136,4 +136,5 @@ private fun Background_Preview_ColorGradientRadial() {
)
}

// We cannot use a network image in Compose previews, so we don't have a preview for Background.Image.
// We cannot use a network image in Compose previews, so we don't have a preview for Background.Image. Instead, we have
// some tests in BackgroundTests.
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
package com.revenuecat.purchases.ui.revenuecatui.components.modifier

import android.graphics.drawable.ColorDrawable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.requiredSize
import androidx.compose.material3.Text
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.semantics.testTag
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onNodeWithTag
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry
import coil.Coil
import coil.ImageLoader
import coil.annotation.ExperimentalCoilApi
import coil.test.FakeImageLoaderEngine
import com.revenuecat.purchases.paywalls.components.common.Background
import com.revenuecat.purchases.paywalls.components.properties.ImageUrls
import com.revenuecat.purchases.paywalls.components.properties.ThemeImageUrls
import com.revenuecat.purchases.ui.revenuecatui.assertions.assertPixelColorEquals
import com.revenuecat.purchases.ui.revenuecatui.assertions.assertPixelColorPercentage
import com.revenuecat.purchases.ui.revenuecatui.components.properties.toBackgroundStyle
import org.junit.After
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.annotation.Config
import org.robolectric.annotation.GraphicsMode
import org.robolectric.shadows.ShadowPixelCopy
import java.net.URL

@GraphicsMode(GraphicsMode.Mode.NATIVE)
@Config(shadows = [ShadowPixelCopy::class], sdk = [26])
@OptIn(ExperimentalCoilApi::class)
@RunWith(AndroidJUnit4::class)
class BackgroundTests {

private enum class TestUrl(val urlString: String, val color: Color) {
Blue(urlString = "https://blue", color = Color.Blue),
;
}

@get:Rule
val composeTestRule = createComposeRule()

@Before
fun setup() {
val engine = FakeImageLoaderEngine.Builder()
.interceptAllTestUrls()
.build()
val imageLoader = ImageLoader.Builder(InstrumentationRegistry.getInstrumentation().targetContext)
.components { add(engine) }
.build()

Coil.setImageLoader(imageLoader)
}

@After
fun teardown() {
Coil.reset()
}

@Test
fun `Should properly set an image background`(): Unit = with(composeTestRule) {
// Arrange
val sizePx = 100
val testUrl = TestUrl.Blue
val expectedColor = testUrl.color
val background = Background.Image(ThemeImageUrls(light = testUrl.toImageUrls(size = sizePx)))
setContent {
val backgroundStyle = background.toBackgroundStyle()
val sizeDp = with(LocalDensity.current) { sizePx.toDp() }

// Act
Box(
modifier = Modifier
.requiredSize(sizeDp)
.background(backgroundStyle)
.semantics { testTag = "box" }
)
}

// Assert
onNodeWithTag("box")
.assertIsDisplayed()
.assertPixelColorEquals(startX = 0, startY = 0, width = sizePx, height = sizePx, color = expectedColor)
}

@Test
fun `Should draw image background behind content`(): Unit = with(composeTestRule) {
// Arrange
val sizePx = 100
val testUrl = TestUrl.Blue
val expectedColor = testUrl.color
val background = Background.Image(ThemeImageUrls(light = testUrl.toImageUrls(size = sizePx)))
setContent {
val backgroundStyle = background.toBackgroundStyle()
val sizeDp = with(LocalDensity.current) { sizePx.toDp() }

// Act
Text(
text = "Hello",
modifier = Modifier
.requiredSize(sizeDp)
.background(backgroundStyle)
.semantics { testTag = "box" }
)
}

// Assert
onNodeWithTag("box")
.assertIsDisplayed()
.assertPixelColorPercentage(
startX = 0,
startY = 0,
width = sizePx,
height = sizePx,
color = expectedColor,
// Text rendering might not be fully deterministic (e.g. due to anti aliasing, font settings, etc.) so
// we're just verifying that the majority of the Composable shows the background, but not all of it.
predicate = { percentage -> percentage in 0.6f..0.99f }
)
}

private fun FakeImageLoaderEngine.Builder.interceptAllTestUrls(): FakeImageLoaderEngine.Builder = apply {
TestUrl.values().forEach { testUrl ->
intercept(testUrl.urlString, ColorDrawable(testUrl.color.toArgb()))
}
}

private fun TestUrl.toImageUrls(size: Int): ImageUrls =
ImageUrls(
original = URL(urlString),
webp = URL(urlString),
webpLowRes = URL(urlString),
width = size.toUInt(),
height = size.toUInt(),
)
}