-
Notifications
You must be signed in to change notification settings - Fork 36
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Introduce support for rendering Compose hierarchies.
- Loading branch information
1 parent
b2bfddc
commit 0d9cf52
Showing
29 changed files
with
1,086 additions
and
33 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
# compose-tests | ||
|
||
Contains the UI tests for Compose support. These can't live in the main radiography module since | ||
they require the Compose compiler to be turned on. No non-test source code should be placed in this | ||
module. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile | ||
|
||
plugins { | ||
id("com.android.library") | ||
kotlin("android") | ||
} | ||
|
||
android { | ||
compileSdkVersion(30) | ||
|
||
compileOptions { | ||
sourceCompatibility = JavaVersion.VERSION_1_8 | ||
targetCompatibility = JavaVersion.VERSION_1_8 | ||
} | ||
|
||
defaultConfig { | ||
minSdkVersion(21) | ||
targetSdkVersion(28) | ||
versionCode = 1 | ||
versionName = "1.0" | ||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" | ||
} | ||
|
||
buildFeatures { | ||
buildConfig = false | ||
compose = true | ||
} | ||
|
||
composeOptions { | ||
kotlinCompilerVersion = Versions.KotlinCompiler | ||
kotlinCompilerExtensionVersion = Versions.Compose | ||
} | ||
} | ||
|
||
tasks.withType<KotlinCompile> { | ||
kotlinOptions { | ||
jvmTarget = "1.8" | ||
freeCompilerArgs = listOf( | ||
"-Xallow-jvm-ir-dependencies", | ||
"-Xskip-prerelease-check", | ||
"-Xopt-in=kotlin.RequiresOptIn" | ||
) | ||
} | ||
} | ||
|
||
dependencies { | ||
// Don't use Versions.KotlinStdlib for Kotlin stdlib, since this module actually uses the Compose | ||
// compiler and needs the latest stdlib. | ||
|
||
androidTestImplementation(project(":radiography")) | ||
androidTestImplementation(Dependencies.AppCompat) | ||
androidTestImplementation(Dependencies.Compose.Material) | ||
androidTestImplementation(Dependencies.Compose.Testing) | ||
androidTestImplementation(Dependencies.Compose.Tooling) | ||
androidTestImplementation(Dependencies.InstrumentationTests.Rules) | ||
androidTestImplementation(Dependencies.InstrumentationTests.Runner) | ||
androidTestImplementation(Dependencies.Truth) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
<manifest package="com.squareup.radiography.test.compose" /> |
275 changes: 275 additions & 0 deletions
275
compose-tests/src/androidTest/java/radiography/test/compose/ComposeUiTest.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,275 @@ | ||
package radiography.test.compose | ||
|
||
import android.view.ViewGroup.LayoutParams | ||
import android.widget.TextView | ||
import androidx.compose.foundation.Box | ||
import androidx.compose.foundation.Text | ||
import androidx.compose.foundation.layout.Column | ||
import androidx.compose.foundation.layout.Row | ||
import androidx.compose.foundation.layout.Stack | ||
import androidx.compose.foundation.layout.size | ||
import androidx.compose.material.Button | ||
import androidx.compose.material.Checkbox | ||
import androidx.compose.runtime.SlotTable | ||
import androidx.compose.runtime.currentComposer | ||
import androidx.compose.ui.Modifier | ||
import androidx.compose.ui.layout.layoutId | ||
import androidx.compose.ui.node.Ref | ||
import androidx.compose.ui.platform.DensityAmbient | ||
import androidx.compose.ui.platform.testTag | ||
import androidx.compose.ui.semantics.SemanticsProperties.AccessibilityLabel | ||
import androidx.compose.ui.semantics.SemanticsProperties.AccessibilityValue | ||
import androidx.compose.ui.semantics.SemanticsProperties.Disabled | ||
import androidx.compose.ui.semantics.SemanticsProperties.Focused | ||
import androidx.compose.ui.semantics.SemanticsProperties.Hidden | ||
import androidx.compose.ui.semantics.SemanticsProperties.IsDialog | ||
import androidx.compose.ui.semantics.SemanticsProperties.IsPopup | ||
import androidx.compose.ui.semantics.SemanticsProperties.TestTag | ||
import androidx.compose.ui.semantics.semantics | ||
import androidx.compose.ui.viewinterop.AndroidView | ||
import androidx.ui.test.createComposeRule | ||
import androidx.ui.test.runOnIdle | ||
import com.google.common.truth.Truth.assertThat | ||
import org.junit.Rule | ||
import org.junit.Test | ||
import radiography.Radiography | ||
import radiography.ViewStateRenderers.DefaultsIncludingPii | ||
import radiography.ViewStateRenderers.DefaultsNoPii | ||
import radiography.compose.ComposeLayoutFilters.skipLayoutIdsFilter | ||
import radiography.compose.ComposeLayoutFilters.skipTestTagsFilter | ||
import radiography.compose.ComposeLayoutRenderers.StandardSemanticsRenderer | ||
import radiography.compose.ComposeLayoutRenderers.composeTextRenderer | ||
import radiography.compose.ExperimentalRadiographyComposeApi | ||
import radiography.compose.scan | ||
|
||
@OptIn(ExperimentalRadiographyComposeApi::class) | ||
class ComposeUiTest { | ||
|
||
@get:Rule | ||
val composeRule = createComposeRule() | ||
|
||
@Test fun when_includingPii_then_hierarchyContainsText() { | ||
composeRule.setContent { | ||
Text("FooBar") | ||
} | ||
|
||
runOnIdle { | ||
val hierarchy = Radiography.scan(viewStateRenderers = DefaultsIncludingPii) | ||
assertThat(hierarchy).contains("FooBar") | ||
assertThat(hierarchy).contains("text-length:6") | ||
} | ||
} | ||
|
||
@Test fun when_noPii_then_hierarchyExcludesText() { | ||
composeRule.setContent { | ||
Text("FooBar") | ||
} | ||
|
||
runOnIdle { | ||
val hierarchy = Radiography.scan(viewStateRenderers = DefaultsNoPii) | ||
assertThat(hierarchy).doesNotContain("FooBar") | ||
assertThat(hierarchy).contains("text-length:6") | ||
} | ||
} | ||
|
||
@Test fun viewSizeReported() { | ||
composeRule.setContent { | ||
val (width, height) = with(DensityAmbient.current) { | ||
Pair(30.toDp(), 40.toDp()) | ||
} | ||
Box(modifier = Modifier.size(width, height)) | ||
} | ||
|
||
val hierarchy = runOnIdle { | ||
Radiography.scan(viewStateRenderers = emptyList()) | ||
} | ||
|
||
assertThat(hierarchy).contains("Box { 30×40px }") | ||
} | ||
|
||
@Test fun zeroSizeViewReported() { | ||
composeRule.setContent { | ||
Box() | ||
} | ||
|
||
val hierarchy = runOnIdle { | ||
Radiography.scan(viewStateRenderers = emptyList()) | ||
} | ||
|
||
assertThat(hierarchy).contains("Box { }") | ||
} | ||
|
||
@Test fun semanticsAreReported() { | ||
composeRule.setContent { | ||
Box(Modifier.semantics { set(TestTag, "test tag") }) | ||
Box(Modifier.semantics { set(AccessibilityLabel, "acc label") }) | ||
Box(Modifier.semantics { set(AccessibilityValue, "acc value") }) | ||
Box(Modifier.semantics { set(Disabled, Unit) }) | ||
Box(Modifier.semantics { set(Focused, true) }) | ||
Box(Modifier.semantics { set(Focused, false) }) | ||
Box(Modifier.semantics { set(Hidden, Unit) }) | ||
Box(Modifier.semantics { set(IsDialog, Unit) }) | ||
Box(Modifier.semantics { set(IsPopup, Unit) }) | ||
} | ||
|
||
val hierarchy = runOnIdle { | ||
Radiography.scan() | ||
} | ||
|
||
assertThat(hierarchy).contains("Box { test-tag:\"test tag\" }") | ||
assertThat(hierarchy).contains("Box { label:\"acc label\" }") | ||
assertThat(hierarchy).contains("Box { value:\"acc value\" }") | ||
assertThat(hierarchy).contains("Box { DISABLED }") | ||
assertThat(hierarchy).contains("Box { FOCUSED }") | ||
assertThat(hierarchy).contains("Box { }") | ||
assertThat(hierarchy).contains("Box { HIDDEN }") | ||
assertThat(hierarchy).contains("Box { DIALOG }") | ||
assertThat(hierarchy).contains("Box { POPUP }") | ||
} | ||
|
||
@Test fun checkableChecked() { | ||
composeRule.setContent { | ||
Checkbox(checked = true, onCheckedChange = {}) | ||
} | ||
|
||
val hierarchy = runOnIdle { | ||
Radiography.scan(viewStateRenderers = listOf(StandardSemanticsRenderer)) | ||
} | ||
|
||
assertThat(hierarchy).contains("Checkbox") | ||
assertThat(hierarchy).contains("Checked") | ||
} | ||
|
||
@Test fun textViewContents() { | ||
composeRule.setContent { | ||
Text("Baguette Avec Fromage") | ||
} | ||
|
||
val hierarchy = runOnIdle { | ||
Radiography.scan(viewStateRenderers = listOf(composeTextRenderer(includeText = true))) | ||
} | ||
|
||
assertThat(hierarchy).contains("text-length:21") | ||
assertThat(hierarchy).contains("text:\"Baguette Avec Fromage\"") | ||
} | ||
|
||
@Test fun textViewContentsEllipsized() { | ||
composeRule.setContent { | ||
Text("Baguette Avec Fromage") | ||
} | ||
|
||
val hierarchy = runOnIdle { | ||
Radiography.scan( | ||
viewStateRenderers = listOf( | ||
composeTextRenderer( | ||
includeText = true, | ||
maxTextLength = 11 | ||
) | ||
) | ||
) | ||
} | ||
|
||
assertThat(hierarchy).contains("text-length:21") | ||
assertThat(hierarchy).contains("text:\"Baguette A…\"") | ||
} | ||
|
||
@Test fun skipTestTags() { | ||
composeRule.setContent { | ||
Box { | ||
Button(modifier = Modifier.testTag("42"), onClick = {}, content = {}) | ||
} | ||
} | ||
|
||
val hierarchy = runOnIdle { | ||
Radiography.scan(viewFilter = skipTestTagsFilter("42")) | ||
} | ||
|
||
assertThat(hierarchy).contains("Box") | ||
assertThat(hierarchy).doesNotContain("Button") | ||
} | ||
|
||
@Test fun skipLayoutIds() { | ||
val layoutId = Any() | ||
|
||
composeRule.setContent { | ||
Box { | ||
Button(modifier = Modifier.layoutId(layoutId), onClick = {}, content = {}) | ||
} | ||
} | ||
|
||
val hierarchy = runOnIdle { | ||
Radiography.scan(viewFilter = skipLayoutIdsFilter { it === layoutId }) | ||
} | ||
|
||
assertThat(hierarchy).contains("Box") | ||
assertThat(hierarchy).doesNotContain("Button") | ||
} | ||
|
||
@Test fun nestedLayouts() { | ||
val slotTable = Ref<SlotTable>() | ||
composeRule.setContent { | ||
slotTable.value = currentComposer.slotTable | ||
|
||
Stack { | ||
Box() | ||
Column { | ||
Box() | ||
Box() | ||
} | ||
Row { | ||
Box() | ||
Box() | ||
} | ||
} | ||
} | ||
|
||
val hierarchy = runOnIdle { | ||
slotTable.value!!.scan() | ||
} | ||
|
||
assertThat(hierarchy).contains( | ||
""" | ||
Stack { } | ||
$BLANK +-Box { } | ||
$BLANK +-Column { } | ||
$BLANK | +-Box { } | ||
$BLANK | `-Box { } | ||
$BLANK `-Row { } | ||
$BLANK +-Box { } | ||
$BLANK `-Box { } | ||
""".trimIndent() | ||
) | ||
} | ||
|
||
@Test fun nestedViewsInsideLayouts() { | ||
val slotTable = Ref<SlotTable>() | ||
composeRule.setContent { | ||
slotTable.value = currentComposer.slotTable | ||
|
||
Box { | ||
AndroidView(::TextView) { | ||
it.layoutParams = LayoutParams(0, 0) | ||
} | ||
} | ||
} | ||
|
||
val hierarchy = runOnIdle { | ||
slotTable.value!!.scan() | ||
} | ||
|
||
// The stuff below the AndroidView is implementation details, testing it is brittle and | ||
// pointless. | ||
assertThat(hierarchy).contains( | ||
""" | ||
Box { } | ||
$BLANK `-AndroidView { } | ||
""".trimIndent() | ||
) | ||
// But this view description should show up at some point. | ||
assertThat(hierarchy).contains("`-TextView { 0×0px, text-length:0 }") | ||
} | ||
|
||
companion object { | ||
private const val BLANK = '\u00a0' | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
<manifest package="com.squareup.radiography.test.compose.empty" /> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
# compose-unsupported-tests | ||
|
||
Tests that the library degrades gracefully in an app that's using an unsupported Compose version. |
Oops, something went wrong.