From d8de4445517858d6f070641581bd150007e5e137 Mon Sep 17 00:00:00 2001 From: Trevor McGuire Date: Mon, 4 Mar 2024 12:51:28 -0800 Subject: [PATCH] Add test to verify we haven't regressed on https://github.com/google/jetpack-camera-app/pull/28 --- .../jetpackcamera/ComposeTestRuleExt.kt | 48 ++++++++++++++++++ .../google/jetpackcamera/NavigationTest.kt | 49 ++++++++++++++----- .../feature/preview/ui/TestTags.kt | 2 +- 3 files changed, 87 insertions(+), 12 deletions(-) diff --git a/app/src/androidTest/java/com/google/jetpackcamera/ComposeTestRuleExt.kt b/app/src/androidTest/java/com/google/jetpackcamera/ComposeTestRuleExt.kt index a7c126d3b..a15afb39c 100644 --- a/app/src/androidTest/java/com/google/jetpackcamera/ComposeTestRuleExt.kt +++ b/app/src/androidTest/java/com/google/jetpackcamera/ComposeTestRuleExt.kt @@ -17,10 +17,13 @@ package com.google.jetpackcamera import android.content.Context import androidx.annotation.StringRes +import androidx.compose.ui.test.SemanticsMatcher import androidx.compose.ui.test.SemanticsNodeInteraction import androidx.compose.ui.test.SemanticsNodeInteractionsProvider import androidx.compose.ui.test.onNodeWithText +import androidx.compose.ui.test.printToString import androidx.test.core.app.ApplicationProvider +import org.junit.AssumptionViolatedException /** * Allows use of testRule.onNodeWithText that uses an integer string resource @@ -31,3 +34,48 @@ fun SemanticsNodeInteractionsProvider.onNodeWithText( ): SemanticsNodeInteraction = onNodeWithText( text = ApplicationProvider.getApplicationContext().getString(strRes) ) + +/** + * Assumes that the provided [matcher] is satisfied for this node. + * + * This is equivalent to [SemanticsNodeInteraction.assert()], but will skip the test rather than + * fail the test. + * + * @param matcher Matcher to verify. + * @param messagePrefixOnError Prefix to be put in front of an error that gets thrown in case this + * assert fails. This can be helpful in situations where this assert fails as part of a bigger + * operation that used this assert as a precondition check. + * + * @throws AssumptionViolatedException if the matcher does not match or the node can no + * longer be found + */ +fun SemanticsNodeInteraction.assume( + matcher: SemanticsMatcher, + messagePrefixOnError: (() -> String)? = null +): SemanticsNodeInteraction { + var errorMessageOnFail = "Failed to assume the following: (${matcher.description})" + if (messagePrefixOnError != null) { + errorMessageOnFail = messagePrefixOnError() + "\n" + errorMessageOnFail + } + val node = fetchSemanticsNode(errorMessageOnFail) + if (!matcher.matches(node)) { + throw AssumptionViolatedException( + buildGeneralErrorMessage(errorMessageOnFail, this) + ) + } + return this +} + +internal fun buildGeneralErrorMessage( + errorMessage: String, + nodeInteraction: SemanticsNodeInteraction +): String { + val sb = StringBuilder() + + sb.appendLine(errorMessage) + + sb.appendLine("Semantics of the node:") + sb.appendLine(nodeInteraction.printToString()) + + return sb.toString() +} diff --git a/app/src/androidTest/java/com/google/jetpackcamera/NavigationTest.kt b/app/src/androidTest/java/com/google/jetpackcamera/NavigationTest.kt index 55d09890e..df6dcc411 100644 --- a/app/src/androidTest/java/com/google/jetpackcamera/NavigationTest.kt +++ b/app/src/androidTest/java/com/google/jetpackcamera/NavigationTest.kt @@ -17,6 +17,7 @@ package com.google.jetpackcamera import android.app.Activity import androidx.compose.ui.test.isDisplayed +import androidx.compose.ui.test.isEnabled import androidx.compose.ui.test.junit4.createEmptyComposeRule import androidx.compose.ui.test.onNodeWithTag import androidx.compose.ui.test.performClick @@ -26,6 +27,7 @@ import androidx.test.platform.app.InstrumentationRegistry import androidx.test.rule.GrantPermissionRule import androidx.test.uiautomator.UiDevice import com.google.jetpackcamera.feature.preview.ui.CAPTURE_BUTTON +import com.google.jetpackcamera.feature.preview.ui.FLIP_CAMERA_BUTTON import com.google.jetpackcamera.feature.preview.ui.SETTINGS_BUTTON import com.google.jetpackcamera.settings.ui.BACK_BUTTON import org.junit.Rule @@ -52,21 +54,17 @@ class NavigationTest { } // Navigate to the settings screen - composeTestRule.onNodeWithTag(SETTINGS_BUTTON).apply { - assertExists() - performClick() - } + composeTestRule.onNodeWithTag(SETTINGS_BUTTON) + .assertExists() + .performClick() // Navigate back using the button - composeTestRule.onNodeWithTag(BACK_BUTTON).apply { - assertExists() - performClick() - } + composeTestRule.onNodeWithTag(BACK_BUTTON) + .assertExists() + .performClick() // Assert we're on PreviewScreen by finding the capture button - composeTestRule.onNodeWithTag(CAPTURE_BUTTON).apply { - assertExists() - } + composeTestRule.onNodeWithTag(CAPTURE_BUTTON).assertExists() // Press the device's back button uiDevice.pressBack() @@ -77,6 +75,35 @@ class NavigationTest { ).assertDoesNotExist() } + // Test to ensure we haven't regressed to the cause of + // https://github.com/google/jetpack-camera-app/pull/28 + @Test + fun returnFromSettings_afterFlipCamera_returnsToPreview() = runScenarioTest { + // Wait for the capture button to be displayed + composeTestRule.waitUntil { + composeTestRule.onNodeWithTag(CAPTURE_BUTTON).isDisplayed() + } + + // If flipping the camera is available, flip it. Otherwise skip test. + composeTestRule.onNodeWithTag(FLIP_CAMERA_BUTTON) + .assume(isEnabled()) { + "Device does not have multiple cameras to flip between." + }.performClick() + + // Navigate to the settings screen + composeTestRule.onNodeWithTag(SETTINGS_BUTTON) + .assertExists() + .performClick() + + // Navigate back using the button + composeTestRule.onNodeWithTag(BACK_BUTTON) + .assertExists() + .performClick() + + // Assert we're on PreviewScreen by finding the capture button + composeTestRule.onNodeWithTag(CAPTURE_BUTTON).assertExists() + } + private inline fun runScenarioTest( crossinline block: ActivityScenario.() -> Unit ) { diff --git a/feature/preview/src/main/java/com/google/jetpackcamera/feature/preview/ui/TestTags.kt b/feature/preview/src/main/java/com/google/jetpackcamera/feature/preview/ui/TestTags.kt index 1b3b8b9fc..d0693eb1d 100644 --- a/feature/preview/src/main/java/com/google/jetpackcamera/feature/preview/ui/TestTags.kt +++ b/feature/preview/src/main/java/com/google/jetpackcamera/feature/preview/ui/TestTags.kt @@ -17,4 +17,4 @@ package com.google.jetpackcamera.feature.preview.ui const val CAPTURE_BUTTON = "CaptureButton" const val SETTINGS_BUTTON = "SettingsButton" -const val DEFAULT_CAMERA_FACING_SETTING = "SetDefaultCameraFacingSwitch" +const val FLIP_CAMERA_BUTTON = "FlipCameraButton"