test: no overlapping cards test at 360dp, 768dp, 1280dp (Closes #32)
Add NoOverlappingCardsTest that renders AttributesPage at compact, medium, and expanded widths, collects getBoundsInRoot() for all attribute and talent cards, and asserts no rectangles intersect. Tests require JVM Compose test runner (will fail in JS browser like the existing ComposeTest — known environment limitation). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,110 @@
|
||||
package org.shahondin1624
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.geometry.Rect
|
||||
import androidx.compose.ui.test.ExperimentalTestApi
|
||||
import androidx.compose.ui.test.getBoundsInRoot
|
||||
import androidx.compose.ui.test.onAllNodesWithTag
|
||||
import androidx.compose.ui.test.runComposeUiTest
|
||||
import androidx.compose.ui.unit.dp
|
||||
import org.shahondin1624.lib.components.TestTags
|
||||
import org.shahondin1624.lib.components.charactermodel.attributespage.AttributesPage
|
||||
import org.shahondin1624.model.EXAMPLE_CHARACTER
|
||||
import org.shahondin1624.model.attributes.AttributeType
|
||||
import org.shahondin1624.theme.LocalWindowSizeClass
|
||||
import org.shahondin1624.theme.WindowSizeClass
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertFalse
|
||||
|
||||
/**
|
||||
* Verifies that attribute and talent cards never overlap at any screen width.
|
||||
* Tests at 360dp (compact/phone), 768dp (medium/tablet), and 1280dp (expanded/desktop).
|
||||
*/
|
||||
@OptIn(ExperimentalTestApi::class)
|
||||
class NoOverlappingCardsTest {
|
||||
|
||||
private val allAttributeTags = AttributeType.entries.map { TestTags.attributeCard(it.name) }
|
||||
private val character = EXAMPLE_CHARACTER
|
||||
|
||||
/**
|
||||
* Collects the bounding rectangles for all attribute and talent cards,
|
||||
* then asserts no two rectangles intersect.
|
||||
*/
|
||||
private fun assertNoOverlap(tag: String, bounds: List<Pair<String, Rect>>) {
|
||||
for (i in bounds.indices) {
|
||||
for (j in i + 1 until bounds.size) {
|
||||
val (tagA, rectA) = bounds[i]
|
||||
val (tagB, rectB) = bounds[j]
|
||||
val overlaps = rectA.overlaps(rectB) &&
|
||||
rectA.width > 0 && rectA.height > 0 &&
|
||||
rectB.width > 0 && rectB.height > 0
|
||||
assertFalse(
|
||||
overlaps,
|
||||
"Cards overlap at $tag width: '$tagA' $rectA intersects '$tagB' $rectB"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun runOverlapTestAtWidth(widthDp: Int) = runComposeUiTest {
|
||||
val sizeClass = WindowSizeClass.fromWidth(widthDp.dp)
|
||||
|
||||
setContent {
|
||||
CompositionLocalProvider(LocalWindowSizeClass provides sizeClass) {
|
||||
Column(modifier = Modifier.width(widthDp.dp).fillMaxSize()) {
|
||||
AttributesPage(character)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
waitForIdle()
|
||||
|
||||
val bounds = mutableListOf<Pair<String, Rect>>()
|
||||
|
||||
// Collect attribute card bounds
|
||||
for (tag in allAttributeTags) {
|
||||
val nodes = onAllNodesWithTag(tag)
|
||||
val count = nodes.fetchSemanticsNodes().size
|
||||
for (i in 0 until count) {
|
||||
val rect = nodes[i].getBoundsInRoot()
|
||||
bounds.add(tag to Rect(
|
||||
left = rect.left.value,
|
||||
top = rect.top.value,
|
||||
right = rect.right.value,
|
||||
bottom = rect.bottom.value
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
// Collect talent card bounds (sample first few visible)
|
||||
val talentTags = character.talents.talents.take(10).map { TestTags.talentCard(it.name) }
|
||||
for (tag in talentTags) {
|
||||
val nodes = onAllNodesWithTag(tag)
|
||||
val count = nodes.fetchSemanticsNodes().size
|
||||
for (i in 0 until count) {
|
||||
val rect = nodes[i].getBoundsInRoot()
|
||||
bounds.add(tag to Rect(
|
||||
left = rect.left.value,
|
||||
top = rect.top.value,
|
||||
right = rect.right.value,
|
||||
bottom = rect.bottom.value
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
assertNoOverlap("${widthDp}dp", bounds)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun noOverlapAtCompactWidth() = runOverlapTestAtWidth(360)
|
||||
|
||||
@Test
|
||||
fun noOverlapAtMediumWidth() = runOverlapTestAtWidth(768)
|
||||
|
||||
@Test
|
||||
fun noOverlapAtExpandedWidth() = runOverlapTestAtWidth(1280)
|
||||
}
|
||||
Reference in New Issue
Block a user