Automatizar las pruebas de navegación con enfoque garantiza que tu app proporcione una experiencia del usuario coherente y predecible en teclados de hardware, pads direccionales y herramientas de accesibilidad. Compose proporciona APIs de prueba integradas para simular la entrada de teclas y verificar los estados de enfoque.
Cómo configurar pruebas de enfoque
Para escribir pruebas de enfoque, usa createComposeRule() de la biblioteca de pruebas de Compose:
@RunWith(AndroidJUnit4::class) class FocusNavigationTest { @get:Rule val composeTestRule = createComposeRule()
Verifica los objetivos de enfoque
Puedes verificar si un elemento de la IU puede recibir el enfoque haciendo clic en él o solicitando el enfoque, y, luego, afirmando su estado con assertIsFocused() y assertIsNotFocused().
@Test fun interactiveElement_isFocusTarget() { composeTestRule.setContent { AppTheme { CardListScreen() } } val firstCard = composeTestRule.onNodeWithTag("card_1") val secondCard = composeTestRule.onNodeWithTag("card_2") // Focus the first element firstCard.performClick() firstCard.assertIsFocused() secondCard.assertIsNotFocused() }
Cómo probar el recorrido del enfoque unidimensional
Simula las pulsaciones de las teclas Tab y Mayúsculas + Tab con performKeyInput para verificar que el enfoque avanza por la IU en el orden de apariencia. Como práctica recomendada, verifica la navegación hacia adelante (Key.Tab) y la navegación hacia atrás (Key.Tab con Key.ShiftLeft presionado) para garantizar la integridad del recorrido bidireccional:
@Test fun tabKey_navigatesInAppearanceOrder() { composeTestRule.setContent { AppTheme { CardListScreen() } } val firstCard = composeTestRule.onNodeWithTag("card_1") val secondCard = composeTestRule.onNodeWithTag("card_2") val thirdCard = composeTestRule.onNodeWithTag("card_3") firstCard.performClick() firstCard.assertIsFocused() // Press Tab -> Moves to second card firstCard.performKeyInput { pressKey(Key.Tab) } secondCard.assertIsFocused() firstCard.assertIsNotFocused() // Press Tab -> Moves to third card secondCard.performKeyInput { pressKey(Key.Tab) } thirdCard.assertIsFocused() // Press Shift + Tab -> Moves backward to second card thirdCard.performKeyInput { withKeyDown(Key.ShiftLeft) { pressKey(Key.Tab) } } secondCard.assertIsFocused() }
Prueba el desplazamiento direccional bidimensional
Simula las teclas de flecha o las teclas direccionales del pad direccional con Key.DirectionDown, Key.DirectionUp, Key.DirectionRight y Key.DirectionLeft.
@Test fun arrowKeys_moveFocusTwoDimensionallyWithoutWrap() { composeTestRule.setContent { AppTheme { GridLayoutScreen() } } val topLeftButton = composeTestRule.onNodeWithTag("btn_top_left") val bottomLeftButton = composeTestRule.onNodeWithTag("btn_bottom_left") topLeftButton.performClick() topLeftButton.assertIsFocused() // Down arrow moves focus downward to bottom-left button topLeftButton.performKeyInput { pressKey(Key.DirectionDown) } bottomLeftButton.assertIsFocused() // Directional keys do not wrap around: pressing Down on bottom element stays focused bottomLeftButton.performKeyInput { pressKey(Key.DirectionDown) } bottomLeftButton.assertIsFocused() }
Prueba el comportamiento del enfoque del campo de texto
Verifica que los campos de texto de una sola línea avancen el enfoque con la tecla Tab, mientras que los campos de texto de varias líneas conserven el enfoque:
@Test fun textField_handlesTabAccordingToLineLimits() { composeTestRule.setContent { AppTheme { FormScreen() } } val singleLineNode = composeTestRule.onNodeWithTag("single_line_field") val multiLineNode = composeTestRule.onNodeWithTag("multi_line_field") val submitButtonNode = composeTestRule.onNodeWithTag("submit_button") // Single-line field advances focus on Tab singleLineNode.performClick() singleLineNode.assertIsFocused() singleLineNode.performKeyInput { pressKey(Key.Tab) } multiLineNode.assertIsFocused() // Multi-line field keeps focus and inserts '\t' multiLineNode.performKeyInput { pressKey(Key.Tab) } multiLineNode.assertIsFocused() submitButtonNode.assertIsNotFocused() // Shift + Tab escapes multi-line field to previous target multiLineNode.performKeyInput { withKeyDown(Key.ShiftLeft) { pressKey(Key.Tab) } } singleLineNode.assertIsFocused() }
Recomendaciones para ti
- Nota: El texto del vínculo se muestra cuando JavaScript está desactivado
- Objetivos de enfoque
- Orden de recorrido del enfoque
- Enfoque en campos de texto
- Agrupa los destinos de enfoque