آزمایش پیمایش کانونی

خودکارسازی آزمایش‌های پیمایش کانونی تضمین می‌کند که برنامه‌تان تجربه کاربری یکپارچه و قابل‌پیش‌بینی در صفحه‌کلیدهای سخت‌افزاری، کلیدهای کنترل، و ابزارهای دسترس‌پذیری ارائه دهد. ‫Compose میاناهای برنامه‌سازی کاربردی آزمایش داخلی برای شبیه‌سازی ورودی کلید و درستی‌سنجی وضعیت‌های تمرکز ارائه می‌دهد.

راه‌اندازی آزمایش‌های تمرکز

برای نوشتن آزمون‌های تمرکز، از createComposeRule() در کتابخانه آزمون Compose استفاده کنید:

@RunWith(AndroidJUnit4::class)
class FocusNavigationTest {
    @get:Rule
    val composeTestRule = createComposeRule()

هدف‌های فوکوس را درستی‌سنجی کنید

می‌توانید با انجام کلیک یا درخواست تمرکز، و سپس با ادعای وضعیت آن بااستفاده از assertIsFocused() و 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()
}

آزمایش پیمایش کانونی یک‌بعدی

بااستفاده از performKeyInput، فشار دادن کلیدهای جهش و کلید تبدیل + جهش را شبیه‌سازی کنید تا مطمئن شوید که تمرکز در رابط کاربری به‌ترتیب ظاهر پیش می‌رود. به‌عنوان روال مطلوب، هم ناوبری به‌جلو (Key.Tab) و هم ناوبری معکوس (Key.Tab با نگه داشتن Key.ShiftLeft) را به‌تأیید برسانید تا از تمامیت پیمایش دوطرفه مطمئن شوید:

@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()
}

آزمایش پیمایش جهت‌دار دوبعدی

بااستفاده از Key.DirectionDown، Key.DirectionUp، Key.DirectionRight، و 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()
}

آزمایش رفتار تمرکز فیلد نوشتاری

تأیید کنید که فیلدهای نوشتاری تک‌خطی با کلید جهش تمرکز را پیش می‌برند، درحالی‌که فیلدهای نوشتاری چندخطی تمرکز را حفظ می‌کنند:

@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()
}