В этой статье мы поговорим о том, что можно проверить у локатора (элемента) и какие вообще есть возможности проверок. Более того, в этой статье я описал упражнения, которые вы можете выполнить и отработать в качестве практики.
Обычно мои статьи были холиварными, а не техническими. Но в этот раз решила выложить что-то действительно техническое и полезное. Тестировщики, налетай!
Проверки локаторов
Разумеется, чаще всего, мы будем проверять элементы. Это могут быть как интерактивные элементы (кнопки, поля ввода, чек-боксы и т.д.), так и статика (картинки, текст, иконки и т.д.).
Каждый элемент на странице может давать пользователю обратную связь:
Поле ввода подсвечивается красным, если введены невалидные данные.
Иконка "Сохранить" неактивна, потому что ты еще не внес изменения в документ.
Цвет всплывающего уведомления красный, потому что произошла ошибка.
Чаще всего нам нужно получить какое-то свойство элемента только для того, чтобы сразу же проверить его значение. Мы хотим убедиться, что отображается правильный текст или элементу присвоен нужный CSS-класс.
С другой стороны, иногда нам нужно все же уметь получать свойства элементов для промежуточных действий. Например, мы хотим посмотреть, сколько сейчас строк в таблице, удалить одну и проверить, что их стало на одну меньше. Чтобы организовать такую проверку, нам нужно вычислить, сколько строк было до того, как мы удалили последнюю. Если в таблице было X строк, то после нажатия должно быть X-1. Найдите икс, получается? И вот тут нам нужна "ручка", которую мы дернем и получим число, без всяких assert.
В этом разделе мы будем говорить о возможностях работы с локатором в двух плоскостях: мы узнаем, как получить свойство элемента и как его проверить.
Текст элемента
Давай начнем с простого примера — получение и проверка текста. Напишем скрипт, который открывает страницу и забирает текст у первого на странице элемента по локатору h2.tn-atom.
const { test, expect } = require("@playwright/test");
test("get text", async ({ page }) => {
await page.goto("https://inzhenerka.tech/");
const description = await page.locator("h2.tn-atom").first().textContent();
console.log(description);
});
В консоль выведется текст: "Делаем обучение интересным, сохраняя авторский подход и помогаем повысить востребованность на рынке труда специалистам". Почему мы увидели его в консоли? Потому что строка console.log(description). А почему в переменной description оказалось значение? Потому что мы использовали метод textContent(), который возвращает нам текст переданного локатора.
Что еще за first()?
Ты мог заметить, что мы используем у локатора какой-то метод first() перед тем, как запросить текст элемента. Нужно ли его применять каждый раз, когда хочешь получить текст элемента? Или что будет, если этот метод не вызвать? Объясняем. Дело в том, что по нашему локатору h2.tn-atom на странице обнаруживается 2 элемента. Playwright в таких случаях выбрасывает ошибку Error: strict mode violation: locator('h2.tn-atom') resolved to 2 elements. Что переводится примерно как "разберись, че ты хочешь, потом приходи, по такому локатору определяется 2 элемента". PW не выбирает за нас, с каким элементом работать, а просто останавливается. Хочешь выполнения кода – давай более точные инструкции. Ведь метод textContent() работает только с одним элементом, а тут их больше.
Итак, что же делать?
Путя всего два.
Путя первый: напиши более точный локатор. И это — хорошая практика. Если у тебя есть возможность, посмотри, что еще можно написать в локаторе, чтобы точно идентифицировать элемент.
Путя второй и последний: объясни PW, какой из найденных элементов нужно выбрать. В нашем случае мы просим взять первый — first. Кстати, угадай, какой по порядку элемент мы получим, если напишем не .first(), а .last()? Раз уж мы умеем обращаться к первому подходящему и последнему подходящему элементу, возникает вопрос: а что, если я хочу обратиться к четвертому из 10 элементов? Или к третьему, и я не знаю, сколько их там будет всего? Здесь у нас есть метод .nth(number), который принимает на вход число — порядковый номер элемента. И да, индексация начинается с 0. Получается, что nth(1) вернет мне второй по порядку элемент?
Ага! А еще получается, что nth(0) – то же самое, что и first()?
Ага! Просто "ферст" читается как-то проще, согласен? Ссылочки на эти методы, если ты их читаешь: nth, first и last.
Итак, мы умеем получать текст элемента. И тут, если ты работал с Selenium, например, тебе уже все понятно: чтобы написать тест, нам нужно получить текст и сравнить его с ожидаемым. Ну как-то так, например:
`const { test, expect } = require("@playwright/test");
test("get text", async ({ page }) => {
await page.goto("https://inzhenerka.tech/");
// сохранили текст элемента в переменную
const description = await page.locator("h2.tn-atom").first().textContent();
// проверили, что текст в переменной равен тому, который мы указали
expect(description).toEqual(
"Делаем обучение интересным, сохраняя авторский подход и помогаем повысить востребованность на рынке труда специалистам"
);
});
И это даже будет работать.С другой стороны, если открыть документацию, можно увидеть вот такое предупреждение:
Разработчики инструмента прямо нам говорят, что, если хотите проверить текст, используйте лучше метод toHaveText(). Мы с тобой в интернете давно и знаем, что в таких ситуациях нужно сразу спрашивать — а чем это лучше?Сначала давай посмотрим, как будет выглядеть наш тест, если мы будем использовать рекомендуемый метод:
const { test, expect } = require("@playwright/test");
test("get text", async ({ page }) => {
await page.goto("https://inzhenerka.tech/");
await expect(page.locator("h2.tn-atom").first()).toHaveText(
"Делаем обучение интересным, сохраняя авторский подход и помогаем повысить востребованность на рынке труда специалистам"
);
});
Смотри, обошлись без переменной description, сразу написали проверку, мол, такой-то элемент должен содержать такой-то текст. В критерии "читаемость" один балл записываем в пользу второго подхода. Заметил, кстати, что теперь у expect() можно вызвать другой набор методов? Это все потому, что на вход мы передали не page, как раньше, а locator. Вот и методы подходящие появились.У toHaveText() есть еще пара трюков в запасе.Во-первых, этот метод умеет сравнивать, игнорируя регистр. Это пригодится тебе, когда выяснится, что в HTML текст написан маленькими буквами, а на странице отображается КАПСОМ (спроси у своего фронтенд-разработчика, как такое можно организовать).
И вот для того, чтобы не собирать шишки, подбирая правильный регистр у ожидаемого результата, мы можем просто передать правильную настройку:
await expect(page.locator("")).toHaveText("ожидаемый текст", {
ignoreCase: true,
});
Вторая интересная фича метода toHaveText() — он умеет проверять текст у группы элементов. Как это работает: если твой локатор определяет не один элемент на странице, а группу (например, список), то и для проверки текста у элементов мы должны передать группу (массив) значений. Метод соотнесет полученные и ожидаемые тексты один к одному. Если что-то не совпадет — ошибка. Примеры:
<!-- вот у нас есть вот такой список на странице -->
<ul>
<li>Билли</li>
<li>Вилли</li>
<li>Дилли</li>
</ul>
// ✓ Вот этот код проверит, что тексты у элементов соответственно равны переданным.
await expect(page.locator("ul > li")).toHaveText(["Билли", "Вилли", "Дилли"]);
// ✖ Вот такая проверка упадет, потому что текстовки идут в другом порядке
await expect(page.locator("ul > li")).toHaveText(["Дилли", "Билли", "Вилли"]);
// ✖ Такой тест тоже упадет, потому что текст одного из элементов не совпадем с ожидаемым
await expect(page.locator("ul > li")).toHaveText([
"Билли",
"Вилли",
"Джонатан?",
]);
// ✖ Вот тут сложнее.
// Тест упадет, потому что локатор собирает не группу элементов (li), а только один (ul).
// Нельзя сравнить текст одного элемента с группой текстов.
await expect(page.locator("ul")).toHaveText(["Text 1", "Text 2", "Text 3"]);
В общем-то, действительно и удобнее, и код более читаемый. Давай так и будем поступать: хотим получить данные — используем метод класса Locator. Хотим проверку — заносим локатор в expect и вызываем интересный нам метод.
Это бесплатная демо-часть урока тренажера по Playwright от INZHENERKA.TECH. Больше обсуждений по теме проходит в нашем сообществе по Playwright в телеграмме
Напомню о цели этой статьи: поделитесь своим мнением о примерах в тексте, как вам? Считаете ли вы их достаточно информативными?