Программирование14 октября 2026 г.

Как выбрать устойчивый локатор в Playwright на примере формы

Автор
Ненашев А.
На чтение
7 мин
Дата обновления
14.10.2026
Старт:В любой момент
Срок обучения:5 часов
JavaScript и TypeScript для автоматизации тестирования
Интенсивный курс по освоению языков JS и TS с фокусом на создание автотестов. Программа охватывает синтаксис, асинхронность, типизацию и популярные инструменты автоматизации.
Подробнее
67 500 ₽67 500 ₽
#COURSE##INNER#

Как выбрать устойчивый локатор в Playwright на примере формы

Устойчивый локатор в Playwright для формы — это не самый короткий селектор, а тот, который переживает перестановку блоков и мелкую версточную правку. Для учебной формы с полем email и кнопкой отправки обычно достаточно простого правила: поле ищем по подписи, кнопку — по роли и доступному имени. Тогда тест опирается на смысл интерфейса, а не на порядок вложенных div.

Как тест должен находить форму по смыслу

Playwright советует начинать с локаторов, которые ближе к тому, как страницу воспринимает пользователь и вспомогательные технологии. Для формы это особенно полезно: человек ищет не «первый input в первом блоке», а поле с подписью Email и кнопку Отправить. Если тест использует тот же ориентир, он меньше зависит от внутренней структуры DOM.

У этого подхода есть практическая причина. Когда верстальщик добавляет подсказку, оборачивает кнопку в новый контейнер или меняет порядок блоков, смысловые ориентиры могут остаться прежними. Подпись все так же называется Email, а кнопка все так же остается кнопкой с именем Отправить. Значит, локатор по label или role переживет изменения лучше, чем цепочка вроде form > div:nth-child(2) > button.

Какие локаторы выбрать для email и кнопки отправки

Для поля email в учебной форме логичный выбор — getByLabel('Email'). Этот локатор ищет элемент формы по связанному тексту подписи. Он подходит именно под задачу заполнения поля, потому что отражает контракт между <label> и <input>, а не случайное расположение узлов на странице.

Для кнопки отправки лучше взять getByRole('button', { name: 'Отправить' }). У обычного <button> роль определяется автоматически, а текст на кнопке становится ее доступным именем. Такой локатор сразу отвечает на два вопроса: мы ищем именно кнопку и именно ту, на которой написано Отправить. Это полезнее, чем просто искать текст Отправить, потому что одинаковый текст может встретиться и в подсказке, и в заголовке, и в ссылке.

Минимальная разметка для такого теста выглядит так:

<form>
  <label for='email'>Email</label>
  <input id='email' name='email' type='email' />
  <button type='submit'>Отправить</button>
</form>

А тест в среде @playwright/test — так:

await page.getByLabel('Email').fill('student@example.com');
await page.getByRole('button', { name: 'Отправить' }).click();

Это не означает, что один тип локатора решает все случаи. Если подписи нет, текст кнопки меняется в зависимости от состояния или на странице несколько одинаковых форм, локатор придется уточнять. Но для нашего вопроса — учебной формы с подписью email и одной кнопкой отправки — это хороший базовый выбор.

Локаторы одной учебной формы

Учебный пример. Возьмем локальную HTML-форму и проверим один сценарий: пользователь вводит адрес student@example.com, нажимает кнопку и видит статус Отправлено: student@example.com. Среда примера — JavaScript и @playwright/test, без сервера, через page.setContent().

Сначала сама форма:

const htmlV1 = `
<form id='subscribe-form'>
  <div class='field'>
    <label for='email'>Email</label>
    <input id='email' name='email' type='email' />
  </div>
  <div class='actions'>
    <button type='button'>Отправить</button>
  </div>
  <p role='status' aria-live='polite'></p>
</form>
<script>
  const form = document.getElementById('subscribe-form');
  const input = document.getElementById('email');
  const status = form.querySelector('[role="status"]');
  form.querySelector('button').addEventListener('click', () => {
    status.textContent = 'Отправлено: ' + input.value;
  });
</script>`;

В такой разметке сработают и смысловые локаторы, и хрупкий структурный селектор. Именно поэтому nth-child часто кажется безобидным: ошибка прячется до первого рефакторинга DOM.

test('смысловые локаторы для формы', async ({ page }) => {
  await page.setContent(htmlV1);

  await page.getByLabel('Email').fill('student@example.com');
  await page.getByRole('button', { name: 'Отправить' }).click();

  await expect(page.getByRole('status'))
    .toHaveText('Отправлено: student@example.com');
});

Для сравнения, хрупкий вариант обычно выглядит так:

await page.locator('form > div:nth-child(1) > input').fill('student@example.com');
await page.locator('form > div:nth-child(2) > button').click();

Пока поле лежит в первом блоке, а кнопка — во втором, тест проходит. Но сам локатор уже зависит не от интерфейса, а от текущей упаковки элементов в контейнеры.

Проверка после перестановки элементов

Теперь изменим только структуру. Добавим подсказку перед полем и поднимем кнопку выше. Смысл для пользователя останется прежним: есть подпись Email, есть кнопка Отправить, статус после клика тоже тот же.

const htmlV2 = `
<form id='subscribe-form'>
  <p class='hint'>Мы отправим письмо на этот адрес.</p>
  <div class='actions'>
    <button type='button'>Отправить</button>
  </div>
  <div class='field'>
    <label for='email'>Email</label>
    <input id='email' name='email' type='email' />
  </div>
  <p role='status' aria-live='polite'></p>
</form>
<script>
  const form = document.getElementById('subscribe-form');
  const input = document.getElementById('email');
  const status = form.querySelector('[role="status"]');
  form.querySelector('button').addEventListener('click', () => {
    status.textContent = 'Отправлено: ' + input.value;
  });
</script>`;

Проверка может выглядеть так:

for (const html of [htmlV1, htmlV2]) {
  await page.setContent(html);
  await page.getByLabel('Email').fill('student@example.com');
  await page.getByRole('button', { name: 'Отправить' }).click();
  await expect(page.getByRole('status'))
    .toHaveText('Отправлено: student@example.com');
}

Проверяемый результат учебного кейса такой: один и тот же тест с getByLabel и getByRole остается рабочим на двух версиях формы, а вариант с nth-child ломается уже после вставки нового абзаца и переноса кнопки. Это и есть практический критерий устойчивости: локатор переживает изменения верстки, если пользовательский смысл элемента не поменялся.

Здесь полезно помнить еще одну деталь Playwright: локатор переиспользуется не как «ссылка на старый DOM-узел», а как способ заново найти актуальный элемент перед действием. Поэтому после перерисовки страницы семантический локатор часто продолжает работать. Но это не защита от всего: если подпись исчезла, кнопка переименована или элемент стал неоднозначным, тест надо уточнить.

Когда нужен дополнительный контракт тестируемости

getByLabel и getByRole не стоит превращать в догму. На странице может быть несколько кнопок Отправить, несколько полей с одинаковой подписью или форма может жить внутри повторяющихся карточек. В таких случаях полезно сначала ограничить область поиска, а потом применять смысловой локатор внутри нее.

const form = page.getByTestId('subscribe-form');
await form.getByLabel('Email').fill('student@example.com');
await form.getByRole('button', { name: 'Отправить' }).click();

Такой прием создает дополнительный контракт тестируемости через data-testid, но не отменяет пользовательскую проверку. Контейнер мы находим по тестовому идентификатору, а реальное действие внутри формы все равно выполняем по подписи и роли. Это хороший компромисс, когда одной семантики мало. Если же доступного имени у элемента нет вообще или оно нестабильно по замыслу интерфейса, тогда прямой getByTestId тоже может быть оправдан, но лучше понимать, что такой тест уже меньше говорит о качестве самой разметки.

Практический вывод для этой формы короткий: сначала попробуйте getByLabel('Email') для поля и getByRole('button', { name: 'Отправить' }) для кнопки, затем проверьте тест на второй версии той же формы после перестановки блоков. Если локатор переживает такую правку, он уже достаточно хорош для повседневной UI-автоматизации. Если нет, не усложняйте CSS-цепочку, а ищите более ясный смысловой ориентир или вводите явный тестовый контракт.

Если хотите добрать практику именно по этой теме, в сохраненном описании каталога курса «JavaScript и TypeScript для автоматизации тестирования» есть подтвержденные темы про UI-автоматизацию, взаимодействие с браузером и локаторы на примере Playwright, а также про архитектуру тестового проекта. Официальная страница курса в исходных данных недоступна, поэтому ориентироваться можно только на сохраненное описание программы.

Курсы

145360
0,0
0 отзывов
JavaScript и TypeScript для автоматизации тестирования
  • В любой момент
  • 5 часов
  • самостоятельно
  • сертификат
67 500 ₽67 500 ₽
Перейти на сайт