Skip to content

Commit

Permalink
Introduce a Snapshotter interface for tests (#2322)
Browse files Browse the repository at this point in the history
* Introduce a Snapshotter interface for tests

I'm attempting to test that changes to the view hierarchy don't
cause unnecessary extra layouts, so I need the parent view to
remain stable across multiple snapshots. This new Snapshotter
interface makes that straightforward.

This is currently only implemented for 'layout' tests. I'll
follow up with 'lazylayout' tests next.

* SpotlessApply

* Snapshotter for UIViewLazyListAsFlexContainerTest

* Revert incremental change
  • Loading branch information
squarejesse authored Sep 25, 2024
1 parent bcf2d73 commit 3b7890d
Show file tree
Hide file tree
Showing 22 changed files with 357 additions and 158 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* Copyright (C) 2024 Square, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package app.cash.redwood.layout.composeui

import androidx.compose.runtime.Composable
import app.cash.paparazzi.Paparazzi
import app.cash.redwood.layout.Snapshotter

class ComposeSnapshotter(
private val paparazzi: Paparazzi,
private val widget: @Composable () -> Unit,
) : Snapshotter {
override fun snapshot(name: String?) {
paparazzi.snapshot(composable = widget, name = name)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,5 @@ class ComposeUiBoxTest(

override fun text(): Text<@Composable () -> Unit> = ComposeUiText()

override fun verifySnapshot(widget: @Composable () -> Unit, name: String?) {
paparazzi.snapshot(composable = widget, name = name)
}
override fun snapshotter(widget: @Composable () -> Unit) = ComposeSnapshotter(paparazzi, widget)
}
Original file line number Diff line number Diff line change
Expand Up @@ -60,11 +60,7 @@ class ComposeUiFlexContainerTest(

override fun text() = ComposeUiText()

override fun verifySnapshot(widget: @Composable () -> Unit, name: String?) {
paparazzi.snapshot(name) {
widget()
}
}
override fun snapshotter(widget: @Composable () -> Unit) = ComposeSnapshotter(paparazzi, widget)

class ComposeTestFlexContainer private constructor(
private val delegate: ComposeUiFlexContainer,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,5 @@ class ComposeUiSpacerTest : AbstractSpacerTest<@Composable () -> Unit>() {
}
}

override fun verifySnapshot(value: @Composable () -> Unit) {
paparazzi.snapshot(composable = value)
}
override fun snapshotter(widget: @Composable () -> Unit) = ComposeSnapshotter(paparazzi, widget)
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,12 @@ abstract class AbstractBoxTest<T : Any> {
height(height)
}

abstract fun verifySnapshot(widget: T, name: String? = null)
abstract fun snapshotter(widget: T): Snapshotter

@Test
fun testEmpty_Defaults() {
val widget = box()
verifySnapshot(widget.value)
snapshotter(widget.value).snapshot()
}

@Test
Expand All @@ -52,7 +52,7 @@ abstract class AbstractBoxTest<T : Any> {
width(Constraint.Wrap)
height(Constraint.Wrap)
}
verifySnapshot(widget.value)
snapshotter(widget.value).snapshot()
}

@Test
Expand All @@ -61,7 +61,7 @@ abstract class AbstractBoxTest<T : Any> {
width(Constraint.Fill)
height(Constraint.Fill)
}
verifySnapshot(widget.value)
snapshotter(widget.value).snapshot()
}

// testChildren
Expand Down Expand Up @@ -322,7 +322,7 @@ abstract class AbstractBoxTest<T : Any> {
},
)
}
verifySnapshot(widget.value)
snapshotter(widget.value).snapshot()
}

@Test
Expand All @@ -345,7 +345,7 @@ abstract class AbstractBoxTest<T : Any> {
},
)
}
verifySnapshot(widget.value)
snapshotter(widget.value).snapshot()
}

@Test
Expand Down Expand Up @@ -388,7 +388,7 @@ abstract class AbstractBoxTest<T : Any> {
},
)
}
verifySnapshot(widget.value)
snapshotter(widget.value).snapshot()
}

@Test
Expand Down Expand Up @@ -431,7 +431,7 @@ abstract class AbstractBoxTest<T : Any> {
},
)
}
verifySnapshot(widget.value)
snapshotter(widget.value).snapshot()
}

@Test
Expand All @@ -444,10 +444,11 @@ abstract class AbstractBoxTest<T : Any> {
children.insert(1, coloredText(text = mediumText(), color = Blue))
children.insert(2, coloredText(text = shortText(), color = Green))
}
verifySnapshot(widget.value, "Margin")
val snapshotter = snapshotter(widget.value)
snapshotter.snapshot("Margin")
redColor.modifier = Modifier
widget.children.onModifierUpdated(0, redColor)
verifySnapshot(widget.value, "Empty")
snapshotter.snapshot("Empty")
}

/** The view shouldn't crash if its displayed after being detached. */
Expand All @@ -459,16 +460,17 @@ abstract class AbstractBoxTest<T : Any> {
horizontalAlignment(CrossAxisAlignment.Start)
verticalAlignment(CrossAxisAlignment.Start)
}
val snapshotter = snapshotter(widget.value)

// Render before calling detach().
widget.children.insert(0, coloredText(MarginImpl(10.dp), mediumText(), Green))
widget.children.insert(1, coloredText(MarginImpl(0.dp), shortText(), Blue))
verifySnapshot(widget.value, "Before")
snapshotter.snapshot("Before")

// Detach after changes are applied but before they're rendered.
widget.children.insert(0, coloredText(MarginImpl(20.dp), longText(), Red))
widget.children.detach()
verifySnapshot(widget.value, "After")
snapshotter.snapshot("After")
}

@Test fun testDynamicWidgetResizing() {
Expand All @@ -479,6 +481,7 @@ abstract class AbstractBoxTest<T : Any> {
horizontalAlignment(CrossAxisAlignment.Start)
verticalAlignment(CrossAxisAlignment.Start)
}
val snapshotter = snapshotter(container.value)

val a = coloredText(text = "AAA", color = Red)
.apply { modifier = HorizontalAlignmentImpl(CrossAxisAlignment.Start) }
Expand All @@ -489,10 +492,10 @@ abstract class AbstractBoxTest<T : Any> {
val c = coloredText(text = "CCC", color = Green)
.apply { modifier = HorizontalAlignmentImpl(CrossAxisAlignment.End) }
.also { container.children.insert(2, it) }
verifySnapshot(container.value, "v1")
snapshotter.snapshot("v1")

b.text("BBB_v2")
verifySnapshot(container.value, "v2")
snapshotter.snapshot("v2")
}

private fun coloredText(modifier: Modifier = Modifier, text: String, color: Int) = text().apply {
Expand Down
Loading

0 comments on commit 3b7890d

Please sign in to comment.