Введение в Тестирование Мобильной Доступности
Тестирование мобильной доступности гарантирует, что приложения могут использовать все, включая людей с нарушениями зрения, слуха, двигательных функций или когнитивными нарушениями. Более миллиарда людей во всем мире живут с какой-либо формой инвалидности, поэтому доступность — это не просто юридическое требование, а фундаментальный аспект инклюзивного дизайна, который расширяет вашу пользовательскую базу и улучшает опыт всех пользователей.
Тестирование мобильной доступности выходит за рамки базового функционального тестирования. Оно требует понимания того, как вспомогательные технологии взаимодействуют с вашим приложением, обеспечения соответствия Руководству по доступности веб-контента (WCAG) и проверки того, что пользователи с ограниченными возможностями могут самостоятельно выполнять все критически важные пользовательские сценарии.
Ставки высоки: недоступные приложения могут столкнуться с судебными исками в соответствии с Законом об американцах с ограниченными возможностями (ADA), Разделом 508 и аналогичными нормативными актами по всему миру. Что еще важнее, недоступные приложения исключают миллионы потенциальных пользователей и наносят ущерб репутации бренда.
Руководства WCAG 2.1 и 2.2 для Мобильных Устройств
Руководство по доступности веб-контента (WCAG) 2.1 представило критерии успеха, специфичные для мобильных устройств, а WCAG 2.2 добавил дополнительные уточнения. Эти руководства организованы вокруг четырех принципов: Воспринимаемость, Управляемость, Понятность и Надежность (POUR).
Ключевые Критерии Успеха WCAG для Мобильных Устройств
Критерий | Уровень | Фокус на Мобильные |
---|---|---|
1.3.4 Ориентация | AA | Поддержка портретного и ландшафтного режимов |
1.3.5 Определение Назначения Ввода | AA | Программное определение назначения полей ввода |
1.4.10 Перекомпоновка | AA | Адаптация контента без горизонтальной прокрутки |
1.4.11 Контраст Нетекстовых Элементов | AA | Контраст 3:1 для компонентов UI и графики |
1.4.12 Интервал Текста | AA | Поддержка пользовательских настроек интервалов текста |
1.4.13 Контент при Наведении/Фокусе | AA | Отклоняемый, наводимый и устойчивый контент |
2.5.1 Жесты Указателя | A | Альтернативы для многоточечных или траекторных жестов |
2.5.2 Отмена Указателя | A | Возможность прерывания или отмены сенсорных действий |
2.5.3 Метка в Имени | A | Визуальные метки соответствуют программным именам |
2.5.4 Активация Движением | A | Альтернативы для ввода, основанного на движении |
2.5.5 Размер Цели | AAA | Минимум 44x44 CSS пикселей для сенсорных целей |
2.5.7 Движения Перетаскивания | AA | Альтернативы с одним указателем для перетаскивания |
2.5.8 Размер Цели (Минимальный) | AA | Минимум 24x24 CSS пикселей для целей |
Уровни Соответствия
- Уровень A: Основные функции доступности (минимальное требование)
- Уровень AA: Отраслевой стандарт и юридическое требование для большинства юрисдикций
- Уровень AAA: Расширенная доступность (золотой стандарт)
Большинство организаций нацелены на соответствие WCAG 2.1 Уровня AA, который охватывает большинство потребностей в доступности без чрезмерной сложности достижения.
Тестирование с VoiceOver на iOS
VoiceOver — это встроенная программа чтения с экрана Apple для iOS и iPadOS. Тестирование с VoiceOver гарантирует, что пользователи с нарушениями зрения могут эффективно навигировать и взаимодействовать с вашим приложением.
Включение VoiceOver
Быстрое Включение: Тройной щелчок боковой кнопки (настроить в Настройки > Универсальный доступ > Быстрая команда универсального доступа)
Ручное Включение: Настройки > Универсальный доступ > VoiceOver > Включить
Основные Жесты VoiceOver
Одиночное Касание - Выбрать и озвучить элемент
Двойное Касание - Активировать выбранный элемент
Смахивание Вправо - Следующий элемент
Смахивание Влево - Предыдущий элемент
Касание Двумя Пальцами - Пауза/возобновление VoiceOver
Смахивание Вверх (3 пальца) - Прокрутка вниз
Смахивание Вниз (3 пальца) - Прокрутка вверх
Жест Ротора - Два пальца вращаются на экране
Контрольный Список Тестирования VoiceOver
1. Тестирование Потока Навигации
// Пример правильной метки доступности
button.accessibilityLabel = "Добавить в корзину"
button.accessibilityHint = "Добавляет товар в корзину покупок"
button.accessibilityTraits = .button
// Плохой пример - отсутствие контекста
button.accessibilityLabel = "Добавить" // Слишком расплывчато
2. Тестирование Навигации с Ротором
Ротор VoiceOver позволяет пользователям навигировать по заголовкам, ссылкам, элементам форм и др. Проверьте что:
- Заголовки правильно помечены с
accessibilityTraits = .header
- Поля форм логически сгруппированы
- Пользовательские действия ротора работают как ожидается
// Создание пользовательского ротора для разделов таблицы
let customRotor = UIAccessibilityCustomRotor(name: "Разделы") { predicate in
// Пользовательская логика навигации
return UIAccessibilityCustomRotorItemResult(
targetElement: nextSection,
targetRange: nil
)
}
accessibilityCustomRotors = [customRotor]
3. Тестирование Динамического Контента
Когда контент обновляется, пользователям VoiceOver нужны уведомления:
// Объявить изменения пользователям программы чтения с экрана
UIAccessibility.post(
notification: .announcement,
argument: "Корзина обновлена, содержит 3 товара"
)
// Уведомить об изменениях макета
UIAccessibility.post(
notification: .layoutChanged,
argument: firstNewElement
)
4. Альтернативный Текст для Изображений
// Декоративные изображения
decorativeImage.isAccessibilityElement = false
// Информативные изображения
productImage.accessibilityLabel = "Синие кроссовки, размер 10"
// Сложные изображения (графики, диаграммы)
chartView.accessibilityLabel = "График доходов"
chartView.accessibilityValue = "Q1: $50K, Q2: $75K, Q3: $100K, Q4: $125K"
Тестирование с TalkBack на Android
TalkBack — это программа чтения с экрана Google для устройств Android. Она обеспечивает голосовую обратную связь и позволяет пользователям навигировать по приложениям, используя жесты и внешние клавиатуры.
Включение TalkBack
Быстрое Включение: Ярлык клавиш громкости (настроить в Настройки > Специальные возможности > TalkBack > Ярлык TalkBack)
Ручное Включение: Настройки > Специальные возможности > TalkBack > Включить
Основные Жесты TalkBack
Одиночное Касание - Исследование касанием
Двойное Касание - Активировать выбранный элемент
Смахивание Вправо - Следующий элемент
Смахивание Влево - Предыдущий элемент
Смахивание Вниз-Вправо - Чтение сверху
Смахивание Вверх-Вправо - Чтение с текущей позиции
Смахивание Влево-Вправо - Навигация назад
Смахивание Вправо-Влево - Домой
Реализация Тестирования TalkBack
1. Описания Контента
// Кнопка с четким описанием
addButton.contentDescription = "Добавить товар в корзину"
// ImageView с осмысленным описанием
productImage.contentDescription = "Красный кожаный кошелек с золотой молнией"
// Декоративные изображения - скрыть от TalkBack
decorativeIcon.importantForAccessibility =
View.IMPORTANT_FOR_ACCESSIBILITY_NO
// Группировка связанных элементов
containerLayout.contentDescription = "Карточка профиля пользователя"
containerLayout.importantForAccessibility =
View.IMPORTANT_FOR_ACCESSIBILITY_YES
2. Пользовательские Действия
Разрешить пользователям выполнять несколько действий над одним элементом:
ViewCompat.addAccessibilityAction(
emailItem,
"Удалить письмо"
) { view, arguments ->
deleteEmail(view)
true
}
ViewCompat.addAccessibilityAction(
emailItem,
"Пометить как прочитанное"
) { view, arguments ->
markAsRead(view)
true
}
3. Объявления Активной Области
// Объявлять изменения динамического контента
statusTextView.accessibilityLiveRegion =
View.ACCESSIBILITY_LIVE_REGION_POLITE
// Для срочных объявлений
errorTextView.accessibilityLiveRegion =
View.ACCESSIBILITY_LIVE_REGION_ASSERTIVE
// Программное объявление
view.announceForAccessibility("Загрузка завершена")
4. Навигация по Заголовкам
// Пометить представление как заголовок для навигации
titleTextView.accessibilityHeading = true
// Или используя ViewCompat
ViewCompat.setAccessibilityHeading(titleTextView, true)
Метки и Подсказки Доступности
Эффективные метки и подсказки доступности имеют решающее значение для того, чтобы пользователи программ чтения с экрана понимали элементы интерфейса.
Лучшие Практики Метка vs. Подсказка
Метки Доступности: Что представляет собой элемент Подсказки Доступности: Что делает элемент при активации
// Пример iOS
saveButton.accessibilityLabel = "Сохранить документ"
saveButton.accessibilityHint = "Сохраняет ваш документ в облачное хранилище"
// Пример Android
saveButton (как обсуждается в [Cross-Platform Mobile Testing: Strategies for Multi-Device Success](/blog/cross-platform-mobile-testing)) (как обсуждается в [Appium 2.0: New Architecture and Cloud Integration for Modern Mobile Testing](/blog/appium-2-architecture-cloud)).contentDescription = "Сохранить документ"
saveButton.tooltipText = "Сохраняет ваш документ в облачное хранилище"
Рекомендации по Меткам
Хорошая Практика | Плохая Практика |
---|---|
“Поиск товаров” | “Поиск” (отсутствие контекста) |
“Фото профиля” | “Изображение” (обобщенно) |
“Рейтинг 5 звезд” | “Звезды” (отсутствует значение) |
“Закрыть диалог” | “X” (неясное действие) |
“Громкость: 75%” | “Ползунок” (отсутствует состояние) |
Контекстно-Зависимые Метки
// Динамические метки на основе состояния
fun updatePlayButtonLabel() {
playButton.contentDescription = when (mediaPlayer.isPlaying) {
true -> "Приостановить аудио"
false -> "Воспроизвести аудио"
}
}
// Корзина с количеством товаров
fun updateCartLabel(itemCount: Int) {
cartButton.contentDescription = when (itemCount) {
0 -> "Корзина покупок, пуста"
1 -> "Корзина покупок, 1 товар"
else -> "Корзина покупок, $itemCount товаров"
}
}
Контраст Цвета и Визуальный Дизайн
WCAG требует достаточного цветового контраста, чтобы гарантировать, что текст и элементы пользовательского интерфейса воспринимаются пользователями с нарушениями зрения.
Требования к Контрасту
Тип Контента | WCAG Уровень AA | WCAG Уровень AAA |
---|---|---|
Обычный текст (< 18pt) | 4.5:1 | 7:1 |
Крупный текст (≥ 18pt или ≥ 14pt жирный) | 3:1 | 4.5:1 |
Компоненты UI и графика | 3:1 | Н/Д |
Отключенные/неактивные элементы | Нет требований | Нет требований |
Тестирование Контраста Цвета
// iOS - Поддержка Dynamic Type с правильным контрастом
extension UIColor {
static func accessibleText(on background: UIColor) -> UIColor {
// Рассчитать яркость и вернуть подходящий цвет текста
let backgroundLuminance = background.luminance()
return backgroundLuminance > 0.5 ? .black : .white
}
func contrastRatio(with color: UIColor) -> CGFloat {
let luminance1 = self.luminance()
let luminance2 = color.luminance()
let lighter = max(luminance1, luminance2)
let darker = min(luminance1, luminance2)
return (lighter + 0.05) / (darker + 0.05)
}
}
Независимость от Цвета
Никогда не полагайтесь исключительно на цвет для передачи информации:
// Плохо: Только цвет указывает состояние
statusIndicator.setBackgroundColor(Color.RED) // Состояние ошибки
// Хорошо: Цвет + иконка + текст
statusIndicator.apply {
setBackgroundColor(Color.RED)
setImageResource(R.drawable.ic_error)
contentDescription = "Ошибка: Соединение не удалось"
}
// Хорошо: Паттерн + цвет для графиков
chartView.apply {
// Использовать разные паттерны/текстуры для серий данных
series1.pattern = Pattern.SOLID
series2.pattern = Pattern.STRIPED
series3.pattern = Pattern.DOTTED
}
Размер и Интервал Сенсорных Целей
Адекватные размеры сенсорных целей гарантируют, что пользователи с двигательными нарушениями могут точно нажимать на интерактивные элементы.
Требования к Размеру
WCAG 2.5.5 (Уровень AAA): 44x44 CSS пикселей (примерно 44x44 точек на iOS, 48x48 dp на Android)
WCAG (как обсуждается в Detox: Grey-Box Testing for React Native Applications) 2.5.8 (Уровень AA): минимум 24x24 CSS пикселей
Рекомендации Платформы:
- Руководство по интерфейсу iOS: минимум 44x44 точек
- Material Design Android: минимум 48x48 dp
Примеры Реализации
// iOS - Обеспечение минимальной сенсорной цели
class AccessibleButton: UIButton {
override var intrinsicContentSize: CGSize {
let size = super.intrinsicContentSize
return CGSize(
width: max(size.width, 44),
height: max(size.height, 44)
)
}
override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
let expandedBounds = bounds.insetBy(dx: -10, dy: -10)
return expandedBounds.contains(point)
}
}
// Android - Минимальная сенсорная цель с ViewCompat
fun ensureMinimumTouchTarget(view: View) {
ViewCompat.setMinimumWidth(view, 48.dp)
ViewCompat.setMinimumHeight(view, 48.dp)
}
// Добавить делегата касания для маленьких кликабельных областей
val parent = smallIcon.parent as View
parent.post {
val delegateArea = Rect()
smallIcon.getHitRect(delegateArea)
// Расширить область касания на 12dp во всех направлениях
delegateArea.inset(-12.dp, -12.dp)
parent.touchDelegate = TouchDelegate(delegateArea, smallIcon)
}
Интервал Между Целями
Поддерживайте адекватный интервал между интерактивными элементами для предотвращения случайных нажатий:
<!-- Пример Разметки Android -->
<LinearLayout
android:orientation="horizontal"
android:padding="8dp">
<Button
android:layout_width="wrap_content"
android:layout_height="48dp"
android:minWidth="48dp"
android:layout_marginEnd="16dp"
android:text="Отменить" />
<Button
android:layout_width="wrap_content"
android:layout_height="48dp"
android:minWidth="48dp"
android:text="Подтвердить" />
</LinearLayout>
Тестирование Навигации с Программой Чтения с Экрана
Эффективная навигация с программой чтения с экрана обеспечивает логический порядок чтения и интуитивно понятные потоки взаимодействия.
Тестирование Порядка Фокуса
// iOS - Пользовательский порядок фокуса
override var accessibilityElements: [Any]? {
get {
return [
titleLabel,
descriptionLabel,
primaryButton,
secondaryButton,
closeButton
]
}
set {}
}
// Пропустить декоративные элементы
decorativeView.isAccessibilityElement = false
// Android - Порядок обхода доступности
titleTextView.accessibilityTraversalBefore = descriptionTextView.id
descriptionTextView.accessibilityTraversalBefore = actionButton.id
// Группировка связанного контента
cardView.apply {
importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_YES
contentDescription = "Карточка товара: ${product.name}, ${product.price}"
}
Тестирование Паттернов Навигации
Тестирование Модального Диалога:
// iOS - Захват фокуса в модальном окне
override func accessibilityPerformEscape() -> Bool {
dismissModal()
return true
}
// Обеспечить перемещение фокуса в модальное окно при показе
UIAccessibility.post(notification: .screenChanged, argument: modalTitle)
Тестирование Бесконечной Прокрутки:
// Объявлять при загрузке нового контента
recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
if (isLastItemVisible && loadingMore) {
recyclerView.announceForAccessibility(
"Загрузка дополнительных элементов"
)
}
}
})
Динамический Тип и Масштабирование Шрифтов
Поддержка динамического типа гарантирует, что текст остается читаемым для пользователей с нарушениями зрения.
Динамический Тип в iOS
// Поддержка Dynamic Type
titleLabel.font = UIFont.preferredFont(forTextStyle: .headline)
titleLabel.adjustsFontForContentSizeCategory = true
// Пользовательские шрифты с масштабированием Dynamic Type
let customFont = UIFont(name: "CustomFont", size: 17)!
bodyLabel.font = UIFontMetrics(forTextStyle: .body)
.scaledFont(for: customFont)
bodyLabel.adjustsFontForContentSizeCategory = true
// Прослушивание изменений размера
NotificationCenter.default.addObserver(
forName: UIContentSizeCategory.didChangeNotification,
object: nil,
queue: .main
) { _ in
self.updateLayout()
}
Масштабируемый Текст в Android
<!-- Использовать единицы sp для размеров текста -->
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="16sp"
android:textAppearance="?attr/textAppearanceBody1" />
// Обработка экстремального масштабирования шрифтов
fun isLargeFontScale(): Boolean {
val fontScale = resources.configuration.fontScale
return fontScale >= 1.3f
}
// Настройка макета для крупного текста
if (isLargeFontScale()) {
linearLayout.orientation = LinearLayout.VERTICAL
} else {
linearLayout.orientation = LinearLayout.HORIZONTAL
}
Тестирование Масштабирования Шрифтов
Тестирование iOS:
- Настройки > Универсальный доступ > Экран и размер текста > Увеличенный текст
- Проверить все размеры от XS до XXXL
- Убедиться, что макет не ломается при экстремальных размерах
Тестирование Android:
- Настройки > Экран > Размер шрифта
- Проверить при 100%, 130%, 160%, 200%
- Использовать
adb shell settings put system font_scale 2.0
Инструменты Сканера Доступности
Автоматизированные сканеры быстро выявляют распространенные проблемы доступности, хотя ручное тестирование остается необходимым.
Инспектор Доступности iOS
Accessibility Inspector Xcode обеспечивает аудит доступности в реальном времени:
# Запуск из Xcode
Xcode > Open Developer Tool > Accessibility Inspector
# Функции:
# - Режим инспекции (наведение на элементы)
# - Вкладка аудита (автоматизированные проверки)
# - Анализатор контраста цвета
# - Просмотрщик иерархии элементов
Проверки Аудита:
- Отсутствующие метки доступности
- Недостаточный контраст цвета
- Маленькие сенсорные цели
- Поддержка Dynamic Type
- Несоответствия трейтов
Тестирование из Командной Строки:
# Запустить аудит доступности из терминала
xcrun simctl spawn booted log stream --predicate \
'subsystem == "com.apple.accessibility"' --level=debug
Сканер Доступности Android
Google Accessibility Scanner — это отдельное приложение, анализирующее UI:
# Установить из Google Play Store
# Или через ADB:
adb install accessibility-scanner.apk
# Использование:
# 1. Включить сканер в настройках Специальных возможностей
# 2. Нажать на плавающую кнопку действия
# 3. Перейти на экран для тестирования
# 4. Нажать на галочку для сканирования
Сканер Выявляет:
- Нарушения размера сенсорной цели
- Текст с низким контрастом
- Отсутствующие описания контента
- Маркировка кликабельных элементов
- Проблемы масштабирования текста
Инструменты Автоматизированного Тестирования Доступности
iOS - XCUITest Accessibility:
func testAccessibility() {
let app = XCUIApplication()
app.launch()
// Проверить доступность элементов
let addButton = app.buttons["Добавить в корзину"]
XCTAssertTrue(addButton.exists)
XCTAssertTrue(addButton.isHittable)
// Проверить метки доступности
XCTAssertEqual(
addButton.label,
"Добавить в корзину"
)
// Проверить минимальную сенсорную цель
let frame = addButton.frame
XCTAssertGreaterThanOrEqual(frame.width, 44)
XCTAssertGreaterThanOrEqual(frame.height, 44)
}
Android - Espresso Accessibility:
import androidx.test.espresso.accessibility.AccessibilityChecks
class AccessibilityTest {
init {
// Включить проверки доступности для всех тестов
AccessibilityChecks.enable()
}
@Test
fun testButtonAccessibility() {
onView(withId(R.id.add_button))
.check(matches(isDisplayed()))
.check(matches(hasContentDescription()))
.check(matches(hasMinimumTouchTarget(48.dp)))
}
}
// Пользовательский matcher для размера сенсорной цели
fun hasMinimumTouchTarget(minSize: Int) = object : TypeSafeMatcher<View>() {
override fun describeTo(description: Description) {
description.appendText("имеет минимальную сенсорную цель $minSize")
}
override fun matchesSafely(view: View): Boolean {
return view.width >= minSize && view.height >= minSize
}
}
Тестирование для Пользователей с Ограниченными Возможностями
Настоящая проверка доступности требует тестирования со вспомогательными технологиями в реалистичных сценариях.
Создание Тестовых Сценариев
Сценарии Нарушения Зрения:
1. Завершить регистрацию пользователя только с VoiceOver/TalkBack
2. Перейти к деталям товара и добавить в корзину
3. Завершить процесс оформления заказа с программой чтения с экрана
4. Искать товары и фильтровать результаты
5. Получить доступ к функциям помощи/поддержки
6. Обновить настройки учетной записи
Сценарии Двигательных Нарушений:
1. Навигация по приложению с использованием внешнего переключателя
2. Заполнение форм с использованием голосового ввода
3. Взаимодействие со всеми элементами управления с большими сенсорными целями
4. Тестирование альтернатив жестов (не требуется мультитач)
5. Проверка продления таймаутов для медленных взаимодействий
Контрольный Список Тестирования:
Область Тестирования | Метод Проверки |
---|---|
Программа Чтения с Экрана | Выполнить критические пути только с VoiceOver/TalkBack |
Голосовое Управление | Навигация и взаимодействие только голосовыми командами |
Управление Переключателем | Управление приложением с помощью внешнего переключателя |
Масштабирование Шрифта | Тестирование при 200% масштабе шрифта, проверка читаемости |
Контраст Цвета | Проверка с помощью анализатора контраста, тестирование в оттенках серого |
Чувствительность к Движению | Отключение анимации, проверка поддержки уменьшенного движения |
Инструменты Вспомогательных Технологий
Вспомогательные Технологии iOS:
- VoiceOver (программа чтения с экрана)
- Voice Control (голосовая навигация)
- Switch Control (ввод с внешнего устройства)
- Zoom (увеличение экрана)
- Reduce Motion
- Increase Contrast
Вспомогательные Технологии Android:
- TalkBack (программа чтения с экрана)
- Voice Access (голосовая навигация)
- Switch Access (ввод с внешнего устройства)
- Magnification
- High contrast text
- Remove animations
Доступность в Конвейерах CI/CD
Интеграция тестирования доступности в CI/CD гарантирует, что регрессии выявляются на ранней стадии и поддерживается соответствие.
Автоматизированное Тестирование Доступности в CI
iOS - Интеграция Fastlane:
# Fastfile
lane :accessibility_tests do
run_tests(
scheme: "YourApp",
devices: ["iPhone 14 Pro"],
only_testing: ["YourAppUITests/AccessibilityTests"]
)
# Запустить пользовательский аудит доступности
sh("ruby scripts/accessibility_audit.rb")
end
# Пользовательский скрипт аудита
def audit_accessibility
violations = []
# Проверить изображения без меток доступности
violations += find_unlabeled_images
# Проверить маленькие сенсорные цели
violations += find_small_touch_targets
# Проверить низкий контраст
violations += find_contrast_violations
if violations.any?
fail "Найдено #{violations.count} нарушений доступности"
end
end
Android - Интеграция Gradle:
// build.gradle.kts
android {
testOptions {
unitTests {
isIncludeAndroidResources = true
}
}
}
dependencies {
androidTestImplementation("androidx.test.espresso:espresso-accessibility:3.5.1")
androidTestImplementation("com.google.android.apps.common.testing.accessibility.framework:accessibility-test-framework:4.0.0")
}
// AccessibilityTest.kt
@RunWith(AndroidJUnit4::class)
class ContinuousAccessibilityTest {
@get:Rule
val activityRule = ActivityScenarioRule(MainActivity::class.java)
@Before
fun setup() {
AccessibilityChecks.enable()
.setRunChecksFromRootView(true)
.setSuppressingResultMatcher(
allOf(
matchesCheck(DuplicateSpeakableTextCheck::class.java),
matchesViews(withId(R.id.allowed_duplicate))
)
)
}
@Test
fun testAllScreensAccessibility() {
// Навигация по всем основным экранам
// Проверки доступности выполняются автоматически
navigateToHome()
navigateToProfile()
navigateToSettings()
}
}
Проверки Доступности Pre-commit
#!/bin/bash
# .git/hooks/pre-commit
echo "Запуск проверок доступности..."
# Проверка отсутствующих меток доступности (iOS)
missing_labels=$(grep -r "UIImage\|UIButton" --include="*.swift" | \
grep -v "accessibilityLabel" | wc -l)
if [ "$missing_labels" -gt 0 ]; then
echo "Предупреждение: Элементы UI потенциально без меток доступности"
fi
# Проверка жестко закодированных цветов (должны использовать семантические цвета)
hardcoded_colors=$(grep -r "UIColor(red:" --include="*.swift" | wc -l)
if [ "$hardcoded_colors" -gt 5 ]; then
echo "Предупреждение: Найдены жестко закодированные цвета. Рассмотрите использование семантических цветов"
fi
# Запуск модульных тестов доступности
npm run test:accessibility
if [ $? -ne 0 ]; then
echo "Тесты доступности не прошли. Пожалуйста, исправьте перед коммитом."
exit 1
fi
echo "Проверки доступности пройдены!"
Панель Отчетов о Доступности
// accessibility-report.js
const fs = require('fs');
const { execSync } = require('child_process');
function generateAccessibilityReport() {
const report = {
timestamp: new Date().toISOString(),
platform: process.env.PLATFORM,
violations: [],
warnings: [],
passed: []
};
// Запустить автоматизированные тесты
const testResults = JSON.parse(
execSync('npm run test:accessibility:json').toString()
);
// Категоризировать результаты
testResults.forEach(result => {
if (result.severity === 'error') {
report.violations.push(result);
} else if (result.severity === 'warning') {
report.warnings.push(result);
} else {
report.passed.push(result);
}
});
// Рассчитать оценку соответствия
const total = report.violations.length +
report.warnings.length +
report.passed.length;
report.complianceScore =
((report.passed.length / total) * 100).toFixed(2);
// Сгенерировать HTML отчет
const html = generateHTMLReport(report);
fs.writeFileSync('accessibility-report.html', html);
// Провалить сборку при обнаружении критических нарушений
if (report.violations.length > 0) {
console.error(`Найдено ${report.violations.length} нарушений доступности`);
process.exit(1);
}
console.log(`Соответствие доступности: ${report.complianceScore}%`);
}
generateAccessibilityReport();
Заключение
Тестирование мобильной доступности — это непрерывный процесс, требующий как автоматизированных инструментов, так и ручной проверки со вспомогательными технологиями. Внедряя комплексные стратегии тестирования доступности—от тестирования с VoiceOver и TalkBack до автоматизированной интеграции в CI/CD—вы гарантируете, что ваши мобильные приложения могут использовать все.
Ключевые выводы:
- Начинайте рано: Интегрируйте доступность с этапа проектирования
- Тестируйте с реальными пользователями: Автоматизированные инструменты выявляют только 30-40% проблем
- Используйте инструменты платформы: VoiceOver, TalkBack, Accessibility Inspector
- Автоматизируйте регрессионное тестирование: Предотвращайте повторное появление ошибок доступности
- Обучайте свою команду: Доступность — это ответственность каждого
Соответствие WCAG — это не только избежание юридических проблем—это создание лучших продуктов, которые обслуживают всех пользователей. Каждое улучшение доступности приносит пользу всем, от пользователей с постоянными нарушениями до тех, кто испытывает временные нарушения или ситуационные ограничения.
Инвестируйте в тестирование доступности, и вы создадите более надежные, инклюзивные и успешные мобильные приложения.