Spec-Zone.ru › Playwright

Тестирование доступности

Введение​

Playwright можно использовать для проверки вашего приложения на различные проблемы доступности.

Вот несколько примеров проблем, которые можно выявить:

  • Текст, который будет трудно читать пользователям с нарушениями зрения из-за плохого контраста цвета с фоном
  • Элементы пользовательского интерфейса и элементы форм без меток, которые могли бы быть идентифицированы считывателем экрана
  • Интерактивные элементы с дублированными идентификаторами, что может сбить с толку вспомогательные технологии

В следующих примерах используется пакет @axe-core/playwright, который добавляет поддержку запуска движка тестирования доступности axe в рамках ваших тестов Playwright.

Отказ от ответственности

Автоматизированные тесты доступности могут обнаруживать некоторые распространённые проблемы доступности, такие как отсутствующие или некорректные свойства. Но многие проблемы доступности можно обнаружить только вручную. Рекомендуется использовать комбинацию автоматизированных тестов, ручных оценок доступности и инклюзивных пользовательских тестов.

Для ручных оценок мы рекомендуем Accessibility Insights для веб-сайтов, бесплатный и открытый инструмент разработчика, который поможет вам оценить веб-сайт на соответствие требованиям WCAG 2.1 AA.

Примеры тестов доступности​

Тесты доступности работают так же, как и любые другие тесты Playwright. Вы можете либо создавать отдельные тестовые случаи для них, либо интегрировать сканирования доступности и проверки в свои существующие тестовые случаи.

Следующие примеры демонстрируют несколько основных сценариев тестирования доступности.

Сканирование всей страницы​

Этот пример демонстрирует, как проверить всю страницу на автоматически обнаруживаемые нарушения доступности. Тест:

  1. Импортирует пакет @axe-core/playwright
  2. Использует обычный синтаксис тестов Playwright для определения тестового случая
  3. Использует обычный синтаксис Playwright для перехода на страницу, подлежащую тестированию
  4. Ожидает AxeBuilder.analyze() для запуска сканирования доступности на странице
  5. Использует обычные проверки Playwright для проверки отсутствия нарушений в результатах сканирования
  • TypeScript
  • JavaScript
import { test, expect } from '@playwright/test';
import AxeBuilder from '@axe-core/playwright'; // 1

test.describe('homepage', () => { // 2
  test('should not have any automatically detectable accessibility issues', async ({ page }) => {
    await page.goto('https://your-site.com/'); // 3

    const accessibilityScanResults = await new AxeBuilder({ page }).analyze(); // 4

    expect(accessibilityScanResults.violations).toEqual([]); // 5
  });
});
const { test, expect } = require('@playwright/test');
const AxeBuilder = require('@axe-core/playwright').default; // 1

test.describe('homepage', () => { // 2
  test('should not have any automatically detectable accessibility issues', async ({ page }) => {
    await page.goto('https://your-site.com/'); // 3

    const accessibilityScanResults = await new AxeBuilder({ page }).analyze(); // 4

    expect(accessibilityScanResults.violations).toEqual([]); // 5
  });
});

Настройка axe для сканирования определенной части страницы​

@axe-core/playwright поддерживает множество параметров конфигурации для axe. Вы можете указать эти параметры, используя шаблон Builder с классом AxeBuilder.

Например, вы можете использовать AxeBuilder.include() для ограничения сканирования доступности только одной определённой частью страницы.

AxeBuilder.analyze() будет сканировать страницу в ее текущем состоянии, когда вы его вызываете. Чтобы сканировать части страницы, которые отображаются в результате взаимодействий с пользовательским интерфейсом, используйте Локаторы для взаимодействия со страницей перед вызовом analyze():

test('navigation menu should not have automatically detectable accessibility violations', async ({
  page,
}) => {
  await page.goto('https://your-site.com/');

  await page.getByRole('button', { name: 'Navigation Menu' }).click();

  // It is important to waitFor() the page to be in the desired
  // state *before* running analyze(). Otherwise, axe might not
  // find all the elements your test expects it to scan.
  await page.locator('#navigation-menu-flyout').waitFor();

  const accessibilityScanResults = await new AxeBuilder({ page })
      .include('#navigation-menu-flyout')
      .analyze();

  expect(accessibilityScanResults.violations).toEqual([]);
});

Сканирование на нарушения WCAG​

По умолчанию axe проверяет множество правил доступности. Некоторые из этих правил соответствуют определённым критериям успеха из Веб-контентных руководств по доступности (WCAG), а другие — правилам «лучшей практики», которые не требуются конкретно ни одним критерием WCAG.

Вы можете ограничить сканирование доступности только теми правилами, которые «помечены» как соответствующие конкретным критериям успеха WCAG, используя AxeBuilder.withTags(). Например, Автоматизированные проверки Accessibility Insights для веб-сайтов включают только правила axe, проверяющие нарушения критериев успеха WCAG A и AA; для соответствия этому поведению вы бы использовали теги wcag2a, wcag2aa, wcag21a, и wcag21aa.

Обратите внимание, что автоматизированное тестирование не может обнаружить все виды нарушений WCAG.

test('should not have any automatically detectable WCAG A or AA violations', async ({ page }) => {
  await page.goto('https://your-site.com/');

  const accessibilityScanResults = await new AxeBuilder({ page })
      .withTags(['wcag2a', 'wcag2aa', 'wcag21a', 'wcag21aa'])
      .analyze();

  expect(accessibilityScanResults.violations).toEqual([]);
});

Полный список тегов правил, поддерживаемых axe-core, можно найти в разделе «Теги Axe-core» документации API axe.

Обработка известных проблем​

Исключение отдельных элементов из сканирования​

Если ваше приложение содержит несколько специфических элементов с известными проблемами, вы можете использовать AxeBuilder.exclude() для их исключения из сканирования до тех пор, пока вы не сможете исправить проблемы.

Это обычно самый простой вариант, но у него есть некоторые существенные недостатки:

  • exclude() исключит указанные элементы и все их потомки. Избегайте его использования с компонентами, содержащими много дочерних элементов.
  • exclude() предотвратит выполнение всех правил для указанных элементов, а не только правил, соответствующих известным проблемам.

Вот пример исключения одного элемента из сканирования в одном конкретном тесте:

test('should not have any accessibility violations outside of elements with known issues', async ({
  page,
}) => {
  await page.goto('https://your-site.com/page-with-known-issues');

  const accessibilityScanResults = await new AxeBuilder({ page })
      .exclude('#element-with-known-issue')
      .analyze();

  expect(accessibilityScanResults.violations).toEqual([]);
});

Если соответствующий элемент используется многократно на многих страницах, рассмотрите использование тестового фикстура для повторного использования одной и той же конфигурации AxeBuilder в нескольких тестах.

Отключение отдельных правил сканирования​

Если ваше приложение содержит множество существующих нарушений определённого правила, вы можете использовать AxeBuilder.disableRules() для временного отключения отдельных правил до тех пор, пока вы не сможете исправить проблемы.

Идентификаторы правил, которые необходимо передать disableRules(), можно найти в свойстве id нарушений, которые вы хотите подавить. Полный список правил axe можно найти в документации axe-core.

test('should not have any accessibility violations outside of rules with known issues', async ({
  page,
}) => {
  await page.goto('https://your-site.com/page-with-known-issues');

  const accessibilityScanResults = await new AxeBuilder({ page })
      .disableRules(['duplicate-id'])
      .analyze();

  expect(accessibilityScanResults.violations).toEqual([]);
});

Использование снимков для разрешения конкретных известных проблем​

Если вы хотите разрешить более детальный набор известных проблем, вы можете использовать Снимки для проверки, что набор существующих нарушений не изменился. Этот подход избегает недостатков использования AxeBuilder.exclude() в ущерб немного большей сложности и хрупкости.

Не используйте снимок всего массива accessibilityScanResults.violations. Он содержит детали реализации соответствующих элементов, такие как фрагмент их отрисованного HTML; если вы включаете их в свои снимки, это сделает ваши тесты склонными к сбоям всякий раз, когда один из компонентов меняется по несвязанной причине:

// Don't do this! This is fragile.
expect(accessibilityScanResults.violations).toMatchSnapshot();

Вместо этого создайте отпечаток рассматриваемого нарушения(ий), содержащий только необходимую информацию для уникальной идентификации проблемы, и используйте снимок отпечатка:

// This is less fragile than snapshotting the entire violations array.
expect(violationFingerprints(accessibilityScanResults)).toMatchSnapshot();

// my-test-utils.js
function violationFingerprints(accessibilityScanResults) {
  const violationFingerprints = accessibilityScanResults.violations.map(violation => ({
    rule: violation.id,
    // These are CSS selectors which uniquely identify each element with
    // a violation of the rule in question.
    targets: violation.nodes.map(node => node.target),
  }));

  return JSON.stringify(violationFingerprints, null, 2);
}

Экспорт результатов сканирования как вложения теста​

Большинство тестов доступности в первую очередь озабочены свойством violations результатов сканирования axe. Однако результаты сканирования содержат больше, чем просто violations. Например, результаты также содержат информацию о правилах, которые прошли проверку, и об элементах, которые axe посчитал имеющими неоднозначные результаты для некоторых правил. Эта информация может быть полезной для отладки тестов, которые не обнаруживают все ожидаемые нарушения.

Чтобы включить все результаты сканирования в ваши результаты теста для целей отладки, вы можете добавить результаты сканирования как вложение теста с помощью testInfo.attach(). Отчётчики затем могут встраивать или связывать полные результаты в ваш вывод тестов.

Следующий пример демонстрирует прикрепление результатов сканирования к тесту:

test('example with attachment', async ({ page }, testInfo) => {
  await page.goto('https://your-site.com/');

  const accessibilityScanResults = await new AxeBuilder({ page }).analyze();

  await testInfo.attach('accessibility-scan-results', {
    body: JSON.stringify(accessibilityScanResults, null, 2),
    contentType: 'application/json'
  });

  expect(accessibilityScanResults.violations).toEqual([]);
});

Использование тестового фикстура для общей конфигурации axe​

Тестовые фикстуры — хороший способ поделиться общей AxeBuilder конфигурацией между многими тестами. Некоторые сценарии, где это может быть полезно:

  • Использование общего набора правил во всех тестах
  • Подавление известного нарушения в общем элементе, который появляется на многих страницах
  • Постоянное прикрепление отдельных отчетов о доступности для многих сканирований

Следующий пример демонстрирует создание и использование тестовой фикстуры, охватывающей каждый из этих сценариев.

Создание фикстуры​

В этом примере фикстура создаёт объект AxeBuilder, предварительно настроенный с общей конфигурацией withTags() и exclude().

  • TypeScript
  • JavaScript
import { test as base } from '@playwright/test';
import AxeBuilder from '@axe-core/playwright';

type AxeFixture = {
  makeAxeBuilder: () => AxeBuilder;
};

// Extend base test by providing "makeAxeBuilder"
//
// This new "test" can be used in multiple test files, and each of them will get
// a consistently configured AxeBuilder instance.
export const test = base.extend<AxeFixture>({
  makeAxeBuilder: async ({ page }, use, testInfo) => {
    const makeAxeBuilder = () => new AxeBuilder({ page })
        .withTags(['wcag2a', 'wcag2aa', 'wcag21a', 'wcag21aa'])
        .exclude('#commonly-reused-element-with-known-issue');

    await use(makeAxeBuilder);
  }
});
export { expect } from '@playwright/test';
// axe-test.js
const base = require('@playwright/test');
const AxeBuilder = require('@axe-core/playwright').default;

// Extend base test by providing "makeAxeBuilder"
//
// This new "test" can be used in multiple test files, and each of them will get
// a consistently configured AxeBuilder instance.
exports.test = base.test.extend({
  makeAxeBuilder: async ({ page }, use, testInfo) => {
    const makeAxeBuilder = () => new AxeBuilder({ page })
        .withTags(['wcag2a', 'wcag2aa', 'wcag21a', 'wcag21aa'])
        .exclude('#commonly-reused-element-with-known-issue');

    await use(makeAxeBuilder);
  }
});
exports.expect = base.expect;

Использование фикстуры​

Для использования фикстуры, замените предыдущие примеры new AxeBuilder({ page }) на новую, определённую фикстуру makeAxeBuilder.

const { test, expect } = require('./axe-test');

test('example using custom fixture', async ({ page, makeAxeBuilder }) => {
  await page.goto('https://your-site.com/');

  const accessibilityScanResults = await makeAxeBuilder()
      // Automatically uses the shared AxeBuilder configuration,
      // but supports additional test-specific configuration too
      .include('#specific-element-under-test')
      .analyze();

  expect(accessibilityScanResults.violations).toEqual([]);
});

© 2024 Microsoft
Licensed under the Apache License, Version 2.0.
https://playwright.dev/docs/accessibility-testing

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API