takahirom / roborazzi

Make JVM Android integration test visible 🤖📸
https://takahirom.github.io/roborazzi/
Apache License 2.0
652 stars 24 forks source link

Capturing coil implemented gifs with Roborazzi #274

Open JPrendy opened 4 months ago

JPrendy commented 4 months ago

Hello,

I am wondering does Roborazzi support coil https://github.com/coil-kt/coil

As I see you have support for generating gif images https://github.com/takahirom/roborazzi?tab=readme-ov-file#generate-gif-image

It would be great if we could take screenshots of gifs themselves with this feature to test the gifs are working correctly.

See my example code below using Roborazzi and Coil where the native image was captured by Roborazzi but the local gif I used being loaded with coil wasn't captured at all.

If Roborazzi doesn't support coil, that is okay, I just thought I would raise it, in case I missed something following the README steps.

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        enableEdgeToEdge()
        setContent {
            Roborazzi_coilTheme {
                Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
                    Greeting(
                        modifier = Modifier.padding(innerPadding)
                    )
                }
            }
        }
    }
}

@Composable
fun Greeting(modifier: Modifier = Modifier) {
    val context = LocalContext.current
    val imageLoader = ImageLoader.Builder(context)
        .components {
            if (Build.VERSION.SDK_INT >= 28) {
                add(ImageDecoderDecoder.Factory())
            } else {
                add(GifDecoder.Factory())
            }
        }
        .build()
    Column {
        Text(
            text = "Hello Android!",
            modifier = modifier
        )
        Image(
            painter = rememberAsyncImagePainter(
                ImageRequest.Builder(context)
                    .data(data = R.drawable.daftpunktocat_thomas)
                    .apply(block = {
                    }).build(), imageLoader = imageLoader
            ),
            contentDescription = null,
            modifier = Modifier
                .height(220.dp)
                .width(200.dp)
        )
        Image(
            painter = painterResource(id = R.drawable.octocat),
            contentDescription = null,
            modifier = Modifier
                .height(220.dp)
                .width(200.dp)
        )
    }
}
@GraphicsMode(GraphicsMode.Mode.NATIVE)
@Config(qualifiers = RobolectricDeviceQualifiers.MediumPhone)
@RunWith(AndroidJUnit4::class)
class MainScreenSnapshotTest {

    @get:Rule
    val composeTestRule = createComposeRule()

    @Test
    fun mainScreen() {
        composeTestRule.setContent {
            Roborazzi_coilTheme{
                Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
                    Greeting(
                        modifier = Modifier.padding(innerPadding)
                    )
                }
            }
        }

        composeTestRule
            .onRoot()
            .captureRoboImage("src/test/screenshots/mainScreen.png")
    }
}

The screen in the emulator

mainScreenEmulator

The screenshot captured by Roborazzi

mainScreen

takahirom commented 4 months ago

@JPrendy Thank you for the report! I came across this slide about animation. This is a Japanese slide. But Could you try to use mainClock? https://speakerdeck.com/sumio/a-collection-of-useful-tips-for-taking-screenshots-in-roborazzi?slide=32

image
RSM-CZE commented 3 months ago

@JPrendy

Coil employs an asynchronous method for loading and displaying GIFs with your approach (which is why the painter has the prefix Async and you have to use an instance of ImageRequest). It's important to note that using onRoot does not ensure that the GIF has been rendered; in fact, based on my experience, content loaded asynchronously by Coil typically isn't displayed immediately with onRoot.

To address this issue, you could follow @takahirom's advice and manually advance the clock before capturing an image. However, this method might lead to inconsistencies, especially with GIFs that "animate" (i.e. change over time).

A more reliable solution is to intercept the image/GIF loading process used by Coil, making it behave as if it loads images/GIFs synchronously. This can be achieved by customising the image loader. For detailed guidance on how to implement this, refer to Coil's documentation on testing: Coil Testing Documentation.