Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -16,21 +16,31 @@
package com.google.jetpackcamera.ui.components.capture

import androidx.activity.ComponentActivity
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.semantics.Role
import androidx.compose.ui.semantics.SemanticsActions
import androidx.compose.ui.semantics.SemanticsProperties
import androidx.compose.ui.test.SemanticsMatcher
import androidx.compose.ui.test.assert
import androidx.compose.ui.test.assertContentDescriptionEquals
import androidx.compose.ui.test.assertWidthIsAtLeast
import androidx.compose.ui.test.assertWidthIsEqualTo
import androidx.compose.ui.test.click
import androidx.compose.ui.test.isNotEnabled
import androidx.compose.ui.test.junit4.accessibility.enableAccessibilityChecks
import androidx.compose.ui.test.junit4.createAndroidComposeRule
import androidx.compose.ui.test.onNodeWithTag
import androidx.compose.ui.test.onRoot
import androidx.compose.ui.test.performSemanticsAction
import androidx.compose.ui.test.performTouchInput
import androidx.compose.ui.test.tryPerformAccessibilityChecks
import androidx.compose.ui.unit.dp
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.google.android.apps.common.testing.accessibility.framework.AccessibilityCheckResult.AccessibilityCheckResultType
import com.google.android.apps.common.testing.accessibility.framework.integrations.espresso.AccessibilityValidator
Expand Down Expand Up @@ -74,6 +84,7 @@ class CaptureButtonTest {
}

composeTestRule.onNodeWithTag(CAPTURE_BUTTON).assertExists()
composeTestRule.onNodeWithTag(CAPTURE_BUTTON_RING_BORDER).assertExists()
composeTestRule.onNodeWithTag(
CAPTURE_BUTTON
).assertContentDescriptionEquals("Capture Photo")
Expand All @@ -82,7 +93,8 @@ class CaptureButtonTest {
composeTestRule.onRoot().tryPerformAccessibilityChecks()

composeTestRule.onNodeWithTag(CAPTURE_BUTTON)
.performSemanticsAction(SemanticsActions.OnClick)
.performTouchInput { click() }
composeTestRule.waitForIdle()
assertThat(imageCaptured).isTrue()

composeTestRule.onNodeWithTag(CAPTURE_BUTTON)
Expand All @@ -107,6 +119,7 @@ class CaptureButtonTest {
}
composeTestRule.onRoot().tryPerformAccessibilityChecks()
composeTestRule.onNodeWithTag(CAPTURE_BUTTON).assertExists()
composeTestRule.onNodeWithTag(CAPTURE_BUTTON_RING_BORDER).assertDoesNotExist()
composeTestRule.onNodeWithTag(
CAPTURE_BUTTON
).assertContentDescriptionEquals("Capture Photo")
Expand Down Expand Up @@ -134,6 +147,7 @@ class CaptureButtonTest {
}
composeTestRule.onRoot().tryPerformAccessibilityChecks()
composeTestRule.onNodeWithTag(CAPTURE_BUTTON).assertExists()
composeTestRule.onNodeWithTag(CAPTURE_BUTTON_RING_BORDER).assertDoesNotExist()
composeTestRule.onNodeWithTag(
CAPTURE_BUTTON
).assertContentDescriptionEquals("Start Video Recording")
Expand Down Expand Up @@ -161,6 +175,7 @@ class CaptureButtonTest {
}
composeTestRule.onRoot().tryPerformAccessibilityChecks()
composeTestRule.onNodeWithTag(CAPTURE_BUTTON).assertExists()
composeTestRule.onNodeWithTag(CAPTURE_BUTTON_RING_BORDER).assertDoesNotExist()
composeTestRule.onNodeWithTag(
CAPTURE_BUTTON
).assertContentDescriptionEquals("Stop Video Recording")
Expand All @@ -186,6 +201,7 @@ class CaptureButtonTest {
}
composeTestRule.onRoot().tryPerformAccessibilityChecks()
composeTestRule.onNodeWithTag(CAPTURE_BUTTON).assertExists()
composeTestRule.onNodeWithTag(CAPTURE_BUTTON_RING_BORDER).assertDoesNotExist()
composeTestRule.onNodeWithTag(
CAPTURE_BUTTON
).assertContentDescriptionEquals("Recording Video")
Expand Down Expand Up @@ -215,4 +231,132 @@ class CaptureButtonTest {
CAPTURE_BUTTON
).assert(isNotEnabled())
}

@Test
fun captureButton_unavailable_exists() {
composeTestRule.setContent {
CaptureButton(
modifier = Modifier.testTag(CAPTURE_BUTTON),
onImageCapture = {},
onStartRecording = {},
onStopRecording = {},
onLockVideoRecording = {},
onIncrementZoom = {},
captureButtonUiState = CaptureButtonUiState.Unavailable
)
}
composeTestRule.onRoot().tryPerformAccessibilityChecks()
composeTestRule.onNodeWithTag(CAPTURE_BUTTON).assertExists()
composeTestRule.onNodeWithTag(CAPTURE_BUTTON_RING_BORDER).assertDoesNotExist()
composeTestRule.onNodeWithTag(CAPTURE_BUTTON).assert(isNotEnabled())
}

@Test
fun captureButton_standard_disableAnimations_capturesImageImmediately() {
var imageCaptured = false
composeTestRule.setContent {
CompositionLocalProvider(
LocalDisableAnimations provides true
) {
CaptureButton(
modifier = Modifier.testTag(CAPTURE_BUTTON),
onImageCapture = { imageCaptured = true },
onStartRecording = {},
onStopRecording = {},
onLockVideoRecording = {},
onIncrementZoom = {},
captureButtonUiState = CaptureButtonUiState.Enabled.Idle(CaptureMode.STANDARD)
)
}
}

composeTestRule.onNodeWithTag(CAPTURE_BUTTON)
.performTouchInput { click() }
composeTestRule.waitForIdle()
assertThat(imageCaptured).isTrue()
}

@Test
fun captureButtonNucleus_standardTap_scalesWithSnappySpring() {
var isTapping by mutableStateOf(false)
composeTestRule.setContent {
CaptureButtonNucleus(
modifier = Modifier.testTag(CAPTURE_BUTTON_NUCLEUS),
captureButtonUiState = CaptureButtonUiState.Enabled.Idle(CaptureMode.STANDARD),
captureButtonSize = 100f,
isTapping = isTapping
)
}

// Idle STANDARD latent scale is 0.80f (80.dp for 100.dp button)
composeTestRule.onNodeWithTag(CAPTURE_BUTTON_NUCLEUS).assertWidthIsEqualTo(80.dp)

// Trigger press in manual clock mode to verify underdamped snappy spring overshoot (> 86.dp)
composeTestRule.mainClock.autoAdvance = false
isTapping = true
composeTestRule.mainClock.advanceTimeByFrame()
composeTestRule.mainClock.advanceTimeBy(80L)
composeTestRule.onNodeWithTag(CAPTURE_BUTTON_NUCLEUS).assertWidthIsAtLeast(86.2.dp)

// Settle to resting pressed scale 0.86f (86.dp for 100.dp button)
composeTestRule.mainClock.autoAdvance = true
composeTestRule.waitForIdle()
composeTestRule.onNodeWithTag(CAPTURE_BUTTON_NUCLEUS).assertWidthIsEqualTo(86.dp)

// Release to return to resting idle scale 0.80f (80.dp)
isTapping = false
composeTestRule.waitForIdle()
composeTestRule.onNodeWithTag(CAPTURE_BUTTON_NUCLEUS).assertWidthIsEqualTo(80.dp)
}

@Test
fun captureButtonNucleus_imageOnlyTap_scales() {
var isTapping by mutableStateOf(false)
composeTestRule.setContent {
CaptureButtonNucleus(
modifier = Modifier.testTag(CAPTURE_BUTTON_NUCLEUS),
captureButtonUiState = CaptureButtonUiState.Enabled.Idle(CaptureMode.IMAGE_ONLY),
captureButtonSize = 100f,
isTapping = isTapping
)
}

// Idle IMAGE_ONLY is 86.dp (IDLE_IMAGE_CAPTURE_SCALE 0.86f * 100f)
composeTestRule.onNodeWithTag(CAPTURE_BUTTON_NUCLEUS).assertWidthIsEqualTo(86.dp)

isTapping = true
composeTestRule.waitForIdle()
// Pressed IMAGE_ONLY is PRESSED_IMAGE_CAPTURE_SCALE (0.93f -> 93.dp)
composeTestRule.onNodeWithTag(CAPTURE_BUTTON_NUCLEUS).assertWidthIsEqualTo(93.dp)

isTapping = false
composeTestRule.waitForIdle()
composeTestRule.onNodeWithTag(CAPTURE_BUTTON_NUCLEUS).assertWidthIsEqualTo(86.dp)
}

@Test
fun captureButtonRing_borderWidthZero_doesNotComposeBorder() {
composeTestRule.setContent {
CaptureButtonRing(
captureButtonSize = 86f,
color = { Color.White },
borderWidth = { 0f }
)
}
composeTestRule.onNodeWithTag(CAPTURE_BUTTON_RING_BORDER).assertDoesNotExist()
}

@Test
fun captureButtonRing_borderWidthGreaterThanZero_composesBorder() {
composeTestRule.setContent {
CaptureButtonRing(
captureButtonSize = 86f,
color = { Color.White },
borderWidth = { 3f }
)
}
composeTestRule.onNodeWithTag(CAPTURE_BUTTON_RING_BORDER).assertExists()
}
}
Comment thread
temcguir marked this conversation as resolved.

private const val CAPTURE_BUTTON_NUCLEUS = "capture_button_nucleus"
Loading
Loading