Skip to content

Commit

Permalink
Add test to verify we haven't regressed on
Browse files Browse the repository at this point in the history
  • Loading branch information
temcguir committed Mar 4, 2024
1 parent 1fb8856 commit d8de444
Show file tree
Hide file tree
Showing 3 changed files with 87 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -31,3 +34,48 @@ fun SemanticsNodeInteractionsProvider.onNodeWithText(
): SemanticsNodeInteraction = onNodeWithText(
text = ApplicationProvider.getApplicationContext<Context>().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()
}
49 changes: 38 additions & 11 deletions app/src/androidTest/java/com/google/jetpackcamera/NavigationTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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()
Expand All @@ -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<MainActivity> {
// 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 <reified T : Activity> runScenarioTest(
crossinline block: ActivityScenario<T>.() -> Unit
) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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"

0 comments on commit d8de444

Please sign in to comment.