cashapp / paparazzi

Render your Android screens without a physical device or emulator
https://cashapp.github.io/paparazzi/
Apache License 2.0
2.22k stars 210 forks source link

HoriozontalPager fails to measure after 1.3.4 update #1474

Closed linean closed 3 weeks ago

linean commented 3 weeks ago

Description When updating Paparazzi from version 1.3.1 to 1.3.4, I noticed that some of the screenshots containing HorizontalPager stopped working. Since HorizontalPager is experimental, I suspect the issue may lie there, but I would like to consult about it here. It seems that some measurements are failing, and these failures appear to be related to the content padding set for the pager.

Steps to Reproduce Run the following test:

import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.pager.HorizontalPager
import androidx.compose.foundation.pager.rememberPagerState
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import app.cash.paparazzi.DeviceConfig
import app.cash.paparazzi.Paparazzi
import com.android.ide.common.rendering.api.SessionParams.RenderingMode.V_SCROLL
import org.junit.Rule
import org.junit.Test

class BrokenHorizontalPagerTest {

    @get:Rule
    val paparazzi = Paparazzi(
        renderingMode = V_SCROLL,
        deviceConfig = DeviceConfig.PIXEL_6.copy(
            softButtons = false,
            screenHeight = 1
        )
    )

    @Test
    fun brokenPager() {
        paparazzi.snapshot {
            HorizontalPager(
                state = rememberPagerState(pageCount = { 3 }),
                // The test doesn't crash without content padding ¯\_(ツ)_/¯
                contentPadding = PaddingValues(horizontal = 32.dp),
                modifier = Modifier.fillMaxWidth(),
                pageContent = {}
            )
        }
    }
}

As a result you'll get:

app.cash.paparazzi.internal.PaparazziLogger$MultipleFailuresException: There were 5 errors:

  java.lang.IllegalArgumentException: maxWidth(-167) must be >= than minWidth(0)
    at androidx.compose.ui.unit.ConstraintsKt.Constraints(Constraints.kt:424)
    at androidx.compose.ui.unit.ConstraintsKt.Constraints$default(Constraints.kt:418)
    at androidx.compose.foundation.pager.PagerMeasurePolicyKt$rememberPagerMeasurePolicy$1$1.invoke-0kLqBqw(PagerMeasurePolicy.kt:132)
    at androidx.compose.foundation.pager.PagerMeasurePolicyKt$rememberPagerMeasurePolicy$1$1.invoke(PagerMeasurePolicy.kt:69)
    at androidx.compose.foundation.lazy.layout.LazyLayoutKt$LazyLayout$3$2$1.invoke-0kLqBqw(LazyLayout.kt:107)
    at androidx.compose.foundation.lazy.layout.LazyLayoutKt$LazyLayout$3$2$1.invoke(LazyLayout.kt:100)
    at androidx.compose.ui.layout.LayoutNodeSubcompositionsState$createMeasurePolicy$1.measure-3p2s80s(SubcomposeLayout.kt:709)
    at androidx.compose.ui.node.InnerNodeCoordinator.measure-BRTryo0(InnerNodeCoordinator.kt:126)
    at androidx.compose.foundation.AndroidOverscroll_androidKt$StretchOverscrollNonClippingLayer$2.invoke-3p2s80s(AndroidOverscroll.android.kt:584)
    at androidx.compose.foundation.AndroidOverscroll_androidKt$StretchOverscrollNonClippingLayer$2.invoke(AndroidOverscroll.android.kt:583)
    at androidx.compose.ui.layout.LayoutModifierImpl.measure-3p2s80s(LayoutModifier.kt:294)

    ...

      java.lang.IllegalStateException: layout state is not idle before measure starts
    at androidx.compose.ui.node.LayoutNodeLayoutDelegate.performMeasure-BRTryo0(LayoutNodeLayoutDelegate.kt:1611)
    at androidx.compose.ui.node.LayoutNodeLayoutDelegate.access$performMeasure-BRTryo0(LayoutNodeLayoutDelegate.kt:36)
    at androidx.compose.ui.node.LayoutNodeLayoutDelegate$MeasurePassDelegate.remeasure-BRTryo0(LayoutNodeLayoutDelegate.kt:620)
    at androidx.compose.ui.node.LayoutNode.remeasure-_Sx5XlM$ui_release(LayoutNode.kt:1145)
    ...

The same test works fine in 1.3.1.

Expected behavior Test passes

Additional information:

linean commented 3 weeks ago

Debugging further I've noticed there is an issue with V_SCROLL configuration that I'm using. It causes HorizontalPager to crash but also breaks many other screenshots.

E.g. this records a screenshot that is 1 pixel wide


    @get:Rule
    val paparazzi = Paparazzi(
        renderingMode = V_SCROLL,
        deviceConfig = DeviceConfig.PIXEL_6.copy(
            softButtons = false,
            screenHeight = 1,
        ),
    )

    @Test
    fun brokenVScroll() {
        paparazzi.snapshot {
            Text(
                text = "Broken V_SCROLL",
                modifier = Modifier.background(Color.White)
            )
        }
    }
linean commented 3 weeks ago

After more digging I've found that hack from https://github.com/cashapp/paparazzi/issues/383 stopped working and now it's recommended to use shrink mode https://github.com/cashapp/paparazzi/pull/497. I'm going to migrate my code.