From 206918ad3bdd3ed3baa8d660c58750eeb103d73f Mon Sep 17 00:00:00 2001 From: smugleafdev Date: Tue, 29 Jul 2025 11:12:05 -0600 Subject: [PATCH] Add two compose steps to click annotated string links --- .../compose/steps/actions/TouchSteps.kt | 11 ++++++ .../tests/compose/TestBasicComposeActivity.kt | 16 ++++++++ .../compose/BasicComposeActivity.kt | 39 +++++++++++++++++++ .../compose/FinalComposeActivity.kt | 6 +-- .../compose/SecondComposeActivity.kt | 6 +-- 5 files changed, 72 insertions(+), 6 deletions(-) diff --git a/compose/src/main/java/com/progressive/kherkin/compose/steps/actions/TouchSteps.kt b/compose/src/main/java/com/progressive/kherkin/compose/steps/actions/TouchSteps.kt index 60ed98c..378c6f8 100644 --- a/compose/src/main/java/com/progressive/kherkin/compose/steps/actions/TouchSteps.kt +++ b/compose/src/main/java/com/progressive/kherkin/compose/steps/actions/TouchSteps.kt @@ -7,6 +7,7 @@ import androidx.compose.ui.test.junit4.ComposeTestRule import androidx.compose.ui.test.onNodeWithTag import androidx.compose.ui.test.onNodeWithText import androidx.compose.ui.test.performClick +import androidx.compose.ui.test.performFirstLinkClick import com.progressive.kherkin.common.testcore.Gherkin /** Finds a node that contains [text] and clicks it. */ @@ -22,4 +23,14 @@ fun Gherkin.ITouchNode(tag: String, composeTestRule: ComposeTestRule) { /** Finds a node with [tag] that contains [text] and clicks it. */ fun Gherkin.ITouchNodeWithText(tag: String, text: String, composeTestRule: ComposeTestRule) { composeTestRule.onNode(hasTestTag(tag).and(hasText(text))).assertHasClickAction().performClick() +} + +/** Finds a node that contains an annotated link string with [text] and clicks it. */ +fun Gherkin.ITouchLinkWithText(text: String, composeTestRule: ComposeTestRule) { + composeTestRule.onNodeWithText(text).performFirstLinkClick() +} + +/** Finds a node that contains an annotated link string with [tag] and clicks it. */ +fun Gherkin.ITouchLinkWithTag(tag: String, composeTestRule: ComposeTestRule) { + composeTestRule.onNodeWithTag(tag).performFirstLinkClick() } \ No newline at end of file diff --git a/sampleapp/src/androidTest/java/com/progressive/sampleapp/tests/compose/TestBasicComposeActivity.kt b/sampleapp/src/androidTest/java/com/progressive/sampleapp/tests/compose/TestBasicComposeActivity.kt index 378cbe3..7e6440b 100644 --- a/sampleapp/src/androidTest/java/com/progressive/sampleapp/tests/compose/TestBasicComposeActivity.kt +++ b/sampleapp/src/androidTest/java/com/progressive/sampleapp/tests/compose/TestBasicComposeActivity.kt @@ -4,6 +4,8 @@ import com.progressive.kherkin.common.testcore.And import com.progressive.kherkin.common.testcore.Given import com.progressive.kherkin.common.testcore.Then import com.progressive.kherkin.common.testcore.When +import com.progressive.kherkin.compose.steps.actions.ITouchLinkWithTag +import com.progressive.kherkin.compose.steps.actions.ITouchLinkWithText import com.progressive.kherkin.compose.steps.actions.ITouchText import com.progressive.kherkin.compose.steps.actions.IWaitToSeeScreen import com.progressive.kherkin.compose.steps.assertion.IShouldSeeText @@ -58,4 +60,18 @@ class TestBasicComposeActivity : SampleBaseIntegrationTestCase() { When.INavigateToScreen(FinalComposeScreen(), composeTestRule) Then.IShouldSeeText("Final Compose Activity", composeTestRule) } + + @Test + fun testAnnotatedLinkClickWithText() { + Given.IRenderScreen(BasicComposeScreen(), composeTestRule) + When.IWaitToSeeScreen(BasicComposeScreen(), composeTestRule) + Then.ITouchLinkWithText("Click this link to visit GitHub.", composeTestRule) + } + + @Test + fun testAnnotatedLinkClickWithTag() { + Given.IRenderScreen(BasicComposeScreen(), composeTestRule) + When.IWaitToSeeScreen(BasicComposeScreen(), composeTestRule) + Then.ITouchLinkWithTag("annotatedLink", composeTestRule) + } } \ No newline at end of file diff --git a/sampleapp/src/main/java/com/progressive/sampleapp/activities/compose/BasicComposeActivity.kt b/sampleapp/src/main/java/com/progressive/sampleapp/activities/compose/BasicComposeActivity.kt index 0ec5e1d..55ba040 100644 --- a/sampleapp/src/main/java/com/progressive/sampleapp/activities/compose/BasicComposeActivity.kt +++ b/sampleapp/src/main/java/com/progressive/sampleapp/activities/compose/BasicComposeActivity.kt @@ -36,7 +36,14 @@ import androidx.compose.ui.platform.LocalFocusManager import androidx.compose.ui.platform.LocalSoftwareKeyboardController import androidx.compose.ui.platform.testTag import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.AnnotatedString +import androidx.compose.ui.text.LinkAnnotation +import androidx.compose.ui.text.SpanStyle +import androidx.compose.ui.text.buildAnnotatedString import androidx.compose.ui.text.input.ImeAction +import androidx.compose.ui.text.style.TextDecoration +import androidx.compose.ui.text.withLink +import androidx.compose.ui.text.withStyle import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import com.progressive.kherkin.sampleapp.R @@ -84,6 +91,7 @@ fun SmallTopAppBar() { TextFieldPrefilled() ScrollBoxes() NavigateButton() + Link() } } } @@ -221,6 +229,37 @@ private fun NavigateButton() { } } +@Composable +private fun Link() { + val annotatedLinkString: AnnotatedString = remember { + buildAnnotatedString { + val style = SpanStyle(color = Color.Black) + val styleCenter = SpanStyle( + color = Color(0xff64B5F6), + textDecoration = TextDecoration.Underline) + + withStyle(style = style) { + append("Click this ") + } + + withLink(LinkAnnotation.Url(url = "https://github.com")) { + withStyle(style = styleCenter) { + append("link") + } + } + + withStyle(style = style) { + append(" to visit GitHub.") + } + } + } + + Column(modifier = Modifier.padding(10.dp)) { + Text(annotatedLinkString, + modifier = Modifier.testTag("annotatedLink")) + } +} + @Preview(showBackground = true) @Composable private fun DefaultPreview() { diff --git a/sampleapp/src/main/java/com/progressive/sampleapp/activities/compose/FinalComposeActivity.kt b/sampleapp/src/main/java/com/progressive/sampleapp/activities/compose/FinalComposeActivity.kt index 034e485..9cef707 100644 --- a/sampleapp/src/main/java/com/progressive/sampleapp/activities/compose/FinalComposeActivity.kt +++ b/sampleapp/src/main/java/com/progressive/sampleapp/activities/compose/FinalComposeActivity.kt @@ -32,14 +32,14 @@ class FinalComposeActivity : ComponentActivity() { modifier = Modifier.fillMaxSize(), color = MaterialTheme.colorScheme.background ) { - SetupContent() + SetupFinalComposeActivityContent() } } } } @Composable -private fun SetupContent() { +private fun SetupFinalComposeActivityContent() { Column { Greeting() BasicButton() @@ -73,5 +73,5 @@ private fun BasicButton() { @Preview(showBackground = true) @Composable private fun DefaultPreview() { - SetupContent() + SetupFinalComposeActivityContent() } \ No newline at end of file diff --git a/sampleapp/src/main/java/com/progressive/sampleapp/activities/compose/SecondComposeActivity.kt b/sampleapp/src/main/java/com/progressive/sampleapp/activities/compose/SecondComposeActivity.kt index ce808ed..7b6f217 100644 --- a/sampleapp/src/main/java/com/progressive/sampleapp/activities/compose/SecondComposeActivity.kt +++ b/sampleapp/src/main/java/com/progressive/sampleapp/activities/compose/SecondComposeActivity.kt @@ -30,14 +30,14 @@ class SecondComposeActivity : ComponentActivity() { modifier = Modifier.fillMaxSize(), color = MaterialTheme.colorScheme.background ) { - SetupContent() + SetupFinalComposeActivityContent() } } } } @Composable -private fun SetupContent() { +private fun SetupFinalComposeActivityContent() { Column { Greeting() BasicButton() @@ -70,5 +70,5 @@ private fun BasicButton() { @Preview(showBackground = true) @Composable private fun DefaultPreview() { - SetupContent() + SetupFinalComposeActivityContent() } \ No newline at end of file