📱

Get Our Mobile App

Take your business learning on the go!

Download on the App StoreGet it on Google Play

3. Playwright c нуля: Локаторы

German Vargin34:04

Transcription

Всем привет! Вы на третьем уроке моего курса Play Right с нуля, и сегодня мы с вами посмотрим, как работают локаторы.

Локаторы нам нужны для того, чтобы находить элементы на сайте, который вы тестируете. То есть, это одно из базовых действий. Неважно, что вы будете использовать для своей автоматизации: Play Right, Selenium, Cypress или что-то ещё, вам всегда нужно будет находить элементы, что-то с ними делать и учиться смотреть, что происходит на странице, сверять полученный результат с ожидаемым. И локаторы нам как раз нужны для того, чтобы находить элементы.

И в этом уроке мы с вами посмотрим, какие Play Right предлагает нам локаторы. У нас опять же есть документация, где прямо описано, какие локаторы можно использовать с примерами, что должно быть в HTML-коде, а чтобы обратиться к тому или иному элементу. Ну, давайте один за другим начнём проходить. Мы уже даже часть из них пробовали, но сегодня попробуем чуть-чуть побольше.

Начнём с вот этого локатора Get by Roll. Если вы помните, в предыдущем уроке у нас там была небольшая проблемка, что этот локатор нам вернул несколько элементов и не знал, что выбрать. Сегодня попробуем уже использовать этот локатор так, чтобы он вернул нам однозначный результат.

Возвращаемся в Py. Я сделаю новый файл Python, назову его как положено, с названием, начинающимся на "test", и давайте "locators.py" прямо так и назову. Может быть, кому-то покажется, что у нас тесты сейчас будут не самые полезные, но наша задача — посмотреть, как работают локаторы и попробовать понаходить разные элементы. Так, поехали.

From Play Right sin Copy Ир нам понадобится снова page, и нам понадобится expect, чтобы смотреть результаты. Начнём с теста, который назовём "locator_roll". Дальше уже стандартные импорты и использование методов, которые нам позволят открыть страницу и найти на ней соответствующий элемент. Будем продолжать тестировать сайт, поэтому идём на page.go_to и у.

Дальше прямо на домашней странице мы попробуем с вами что-нибудь найти. Page.get_by_roll. И если вы помните, если вы смотрели предыдущий урок, если нет, то вернитесь назад, посмотрите. Мы пытались в прошлый раз найти вот этот вот элемент "аудиокниги", но локатор нашёл несколько таких ссылок и не знал дальше, куда переходить. Попробуем использовать какую-нибудь ссылку. Вот, по-моему, вот эти вот разделы, которые здесь идут: "читают сейчас". Давайте посмотрим вот этот вот элемент, и тоже ссылка. Возьмём вот её текст и попробуем её найти. Соответственно, здесь by_roll делаем, что это link. Дальше имя передадим как раз то, что мы скопировали: "читают сейчас". И после того, как мы эту ссылку найдём, мы хотим на неё нажать. Делаем click. И в таком случае, если у нас всё сработает, мы должны перейти на эту страницу. И давайте, чтобы ещё раз освежить в памяти, что здесь можно посмотреть, мы проверим title на этой странице. Тоже уже что должно быть, ваш знакомый, вам знакомо. Сейчас мы закрепим. Вот он, title. Соответственно, здесь давайте напишем expect(page).to_have_title("аудиокниги — слушать онлайн бесплатно")". Всё, тест готов. Давайте попробуем его запустить.

Ага, сейчас ещё один момент. Смотрите, у нас запускается три теста. Сейчас я объясню, почему, и объясню, как это немножко оптимизировать. Всё, три теста у нас запустилось. Три теста почему у нас запустилось три теста? Потому что у нас есть здесь другой файл, который мы сделали в прошлый раз, где два теста никуда не делись, они так и остались. Если мы делаем команду `pytest --headed`, то запускается абсолютно всё. Чтобы этого избежать, мы попробуем запускать именно наш файл, который мы вот сейчас пишем с нуля, то есть `test_locators.py`. Для этого можно перейти в, давайте я сейчас покажу, где мы находимся. Надеюсь, у меня OBS записывает терминал тоже. То есть, я нахожусь сейчас в папке `playwright-demo`. Спущусь на папку ниже, `tests`. И теперь мы видим, что мы видим вот эти вот наши файлы: `test_locators.py` и `test_titles.py`. Я вот не хочу запускать `test_titles`, хочу запускать только `test_locators`. Поэтому вот в этой команде `pytest --headed` я передам ещё один параметр: `test_locators`, то есть имя файла, который я хочу запускать, который я хочу тестировать. Запускаю. И как мы видим, у нас уже запускается только один тест.

Сделаю `clear` на всякий случай. Проверю, что у нас на неправильный тайтл реагирует. И сейчас тест должен упасть, потому что я поменял, он не должен совпасть с тем, что ожидается в тесте. Да, всё, у нас вот один тест, всё замечательно работает. Возвращаю и продолжаем с локаторами.

Мы с вами посмотрели, как находить локатор с ролью `link`, то есть ссылки, нажимать. Давайте попробуем ещё какой-нибудь локатор определить, например, например, какую-нибудь кнопку. Давайте намм какую, вот, например, эту кнопку найти. Да, просто убедиться, что у нас она нажимается, нажимается, и вот мы какую-нибудь вот такую страничку с вами увидим. Попробуем посмотреть, что у нас вот рекомендованный какой-нибудь автор отображается. Поехали, продолжим прямо этот же тест. Хорошо.

Пишем `page.get_by_role`. Здесь мы уже напишем `button`. Name и найти соответственно. Мы найдём эту кнопку и также сделаем `click`. А сделаем `click` и после этого, поскольку здесь у нас тайтл страниц не меняется, URL не меняется, что мы попробуем сделать? Мы попробуем найти вот вот текст. Да, давайте сейчас посмотрю, что у нас нигде Стивена больше нету, например, да? Всё, на странице у меня по умолчанию книжки никакие не отображаются. То есть, я предполагаю, что если я нажму "найти", у меня выпадает вот эта вот формочка, и я хочу убедиться, например, что здесь Стивен Кинг присутствует. И для этого мы ещё один локатор, тип локатора используем. Это попробуем на сайте найти текст. `page.get_by_text`. И здесь мы передадим "Стивен Кинг" как параметр в этот метод. Давайте покажу, как это выглядит на практике.

`page`. А подождите, у нас же это будет `expect`. То есть, мы делаем даже вот так вот: `expect`. Дальше `page.get_by_text`. Дальше здесь мы напишем "Стивен Кинг" и тоже проверим, что он присутствует. Это называется `visible`, да? То есть, мы уже здесь пишем наш ожидаемый результат, как вот тут мы проверяли `title`. То здесь мы вот находим сначала локатором `get_by_text` "Стивен Кинг" и `to_be_visible`. Всё, я думаю, готово. Сейчас я уберу это красный ужас. Запускаю. Опять же, у нас один тест запускается, и всё отлично. Всё `passed` за 3 секунды. Давайте как-нибудь я это переименую тоже, чтобы проконтролировать, что у нас действительно это работает. Сейчас должен тест упасть. Замедлился, хороший признак. Да, всё падает. Возвращаю, чтобы работало.

Вот мы с вами уже разобрали два локатора: `get_by_roll`, когда мы находим элемент по какому-то типу (кнопка, ссылка или ещё что-то), либо `get_by_text`, когда мы ищем текст на странице. Отлично.

Смотрим, что у нас ещё есть интересно в документации. `Get by Label`. Я думаю, сейчас я замучаюсь искать лейблы на сайте. Ещё есть `placeholder`. Они на самом деле очень идентично работают. Смотрите, в одном случае был `role`, здесь вот `placeholder`. Мы просто ищем по какому-то атрибуту в HTML этот элемент. Ну, давайте сейчас посмотрим, что у нас здесь есть. Что у нас тут есть? Так, мне надо найти какой-нибудь элемент, который будет с либо с лейблом, либо с плейсхолдером. Ну, вот сразу получилось, да. Смотрите, у меня есть формочка "искать" и у него есть плейсхолдер, который так и называется. Давайте попробуем зайти в это поле ввода, найти его и, и, и, не знаю, может быть, попробовать нажать сюда, что-нибудь введём, какой-нибудь поиск. Давайте, я не знаю, "Игра престолов", как-нибудь вот так вот мы сделаем, нажмём. Да, у нас вот что-нибудь вот так отобразится, и посмотрим, что у нас вот результаты поиска по нашему запросу отображаются. Вот такой тест мы сейчас попробуем заав. Поехали.

Делаем следующий тест. Кстати, пишите комментарии. Я вот раньше нечасто записывал видео с кодом, и как вам комфортно читать? Не комфортно? Быстро я говорю? Не быстро? Достаточно подробно объясняю или наоборот, меньше нужно деталей? Так, поехали.

`test_locator_placeholder`. `page`. `page`. `page.go_to`. Всё уже нам знакомое. Можем прям даже целиком копировать какие-то строчки. Ничего страшного, что мы пока повторяем какой-то код. В процессе я вам тоже покажу, как это оптимизируется. Наша задача сейчас разобраться с локаторами. И вот этой строчкой мы как раз сейчас сделаем `get_by_placeholder`. Обожаю, мне прям подсказывает то, что мне нужно. И в кавычках передаётся, как он у нас там назывался. Он у нас назывался "искать на ЛитРес". Мы его прямо копируем, вставляем. И, и, и, смотрите, мы сюда. У нас что получается с этим действием? Мы нашли локатор, да? И нам здесь нужно передать какое-то значение. Мы это уже с вами раньше делали. И для этого есть команда `fill`. Соответственно, здесь будет наш запрос, который мы хотим записать: "Игра престолов". Вот так вот, да. И "Игра престолов". Соответственно, мы вводим. И дальше нам нужно что-то нажать, да? Здесь можно либо нажать на кнопку "найти" на сайте, либо нажать `Enter`. То есть, вот мы ввели, а он даже автоматически сейчас нам что-то выдаст. Но в любом случае, когда я ввожу что-то ручками, да, то есть, вот он вот так вот и останется. Мне нужно либо нажать на кнопку, либо нажать `Enter`, чтобы что-то отработало. Давайте я вам покажу, как сделать команду нажать `Enter`. Соответственно, это тоже делается через. Дальше есть такая команда `keyboard.press` и в двойных кавычках здесь можно просто сказать: "нажми Enter". Всё. И таким образом у нас Play Right нажмёт `Enter`, и мы увидим то, что должны увидеть.

Дальше пишем наш ожидаемый результат. Соответственно, это `expect`. Уже нам знакомый `page`. Нам снова надо найти элемент. Давайте `get_by_text`. И мы хотим увидеть, увидеть, увидеть, увидеть результаты поиска "Игра престолов". Вот этот вот текст. Единственное, что, наверное, здесь надо как-то, чтобы регистр был у нас соблюдён. Сейчас "Игра престолов". Вот так вот. Всё, я думаю, оно должно отработать. Давайте, поехали.

`pytest`. Запускаем наши тесты. Сейчас у нас сработают снова два теста: первый, который мы написали про `role`, и второй, который мы написали про `placeholder`. Всё отработала, да? Всё замечательно. Всё вернулось. "Игра Престолов", "Игра Престолов" нашлась. И поздравляю, мы с вами выучили ещё один локатор, плюс научились нажимать на кнопку `Enter`.

Так, давайте посмотрим, что у нас ещё есть. Опять же, не вижу смысла повторять то же самое с `label`, потому что абсолютно то же самое, просто другой атрибут используется. Давайте мы, давайте мы посмотрим вот эту вот историю: `get_by_test_id`. Почему я её выделяю? Потому что это хорошая практика. Когда вы работаете совместно с разработкой и говорите разработчикам, как они могут помочь вам с автоматизацией, и часто бывает такое, что какой-то фреймворк, который используют разработчики для построения веб-сайта, у него как-то произвольно строятся вот эти вот элементы, где-то есть одни атрибуты, где-то есть другие, они могут меняться, они могут быть вообще какие-то динамические. Но для автоматизации, чтобы нам легче жилось, нам здорово иметь вот такой элемент, как `data-test-id` или в разных проектах он может как-то по-другому называться: `test-id` ещё что-нибудь. В общем, идентификатор отдельный для тестировщиков, который они могут использовать для своей автоматизации. И когда вам в следующий раз скажут, что надо автоматизировать, а вот такой вещи нету, вы можете разработчиков прямо попрессовать, чтобы они вам это сделали, чтобы опять же вам было проще автоматизировать.

На ЛитРес я точно видел `test_id`. Давайте мы сейчас какой-нибудь элемент выберем, например, "каталог". И вот видите, `data-test-id`. Да, как будто под под Playwright это всё писалось. Опять же, в Playwright можно переименовать и перенастроить, что он будет смотреть не на `data-test-id`, а на какой-то ваш собственный `test_id`, который у вас разработан. В общем, полезная штука. Давайте мы на ней тоже попробуем рассмотреть какой-нибудь пример.

А сделаем ещё один тест. `def test_locator_data_test_id(page):`. И давайте я её как-нибудь назову `data_test_id`. Снова `page`. `page`. И чтобы мы тут могли с вами проверить, ну, давайте попробуем, попробуем нажать на какую-нибудь кнопку ещё. Вот, вот, например, "войти". Да, то есть, мы попробуем нажать на. У нас отображается вот такая формочка "войти", и мы тоже нам что-нибудь, какой-нибудь текст либо кнопку. Вот какой-нибудь такой тест мы сейчас с вами сделаем.

`page.go_to`. И снова я копирую URL. Прямо скоро я вам покажу, как на этом сэкономить время. `less`. `page.get_by_test_id`. И что у нас там с? Сейчас одну секунду. Где же эта кнопка-то? Вот она, "войти". Вот она, ссылка "логин", и вот её `data-test-id`: `tlink`. Немножко странный идентификатор, честно говоря. Ну, давайте попробуем. Соответственно, делаем `click`. И попробуем опять же, сейчас не будем усложнять, вот найти вот такой текст: "Введите логин или почту". У нас что-то должно отобразиться. Соответственно, пишем наш `expect`. `page.get_by_text`. Снова "Введите логин". `to_be_visible`. И кстати, я увидел наверху тоже, мы забыли сделать этот `visible`. Может, у нас ещё не работает? Сейчас проверим. Вот, да, `visible`. Всё, погнали.

Запускаем все тесты. Сейчас у нас уже будет три теста. И один у нас упал. Так, один у нас упал. Один у нас упал. Ага, `resolved` четыре элемента, он говорит. Всё-таки где-то, да, да, `tlink`. Вот этот вот не сработал. Дурацкий идентификатор, кстати, друзья. Это баг, на мой взгляд, это баг. Да, то есть, смотрите, у нас получается, что несколько элементов, несколько кнопок есть с одним и тем же `data-test-id`. Сейчас одну секундочку, я это проверю. Так, `tlink`. Ну, да, да, смотрите, у нас вот все кнопки "мои книги", наверняка "корзина" тоже, у них один и тот же `test_id`: `tlink`. То есть, это недоработка. Ну, на мой взгляд, это недоработка разработчиков сайта. ЛитРес. Здесь они немножко ошиблись, конечно. Элементы должны иметь уникальные идентификаторы. Окей, давайте сейчас я вам какой-нибудь другой, другой какой-нибудь `test_id` найду. Например, вот давайте мы вот сюда вот нажмём "каталог". Надеюсь, здесь уже всё у нас уникально. Вот `header_catalog_button`. Вот это уже похоже на правду.

Так, смотрите, мы теперь переделаем наш тест не потому, что у нас не работает идентификатор, а потому, что сайт криво работает. Мы нажмём на вот эту кнопку и проверим тоже, что здесь есть "бесплатные книги". Упс, да. Так, давайте я тогда `test_id` поменяю. `tlink` на кнопку вот эту вот, которая "каталог", и текст буду искать, искать текст вот этот: "бесплатные книги". "бесплатные книги". Вот, да. Теперь должно, должно всё отработать.

Нас три из трёх теста запустился. Первый запустился, второй. И что-то на третьем у нас опять застряло. Без, потому что печатку сделал. "бесплатные", "бесплатные". Запускаю ещё раз. Сейчас должны мы этот локатор победить. Хотел вам показать `test_id`, и всё пошло не по плану. Да, всё, три из трёх теста отработали. Три из трёх теста отработали. Всё замечательно. Даже посмотрели, что у нас на печатку реагирует. "Игра престолов" тоже у нас сработало.

И смотрите, что я вам ещё пока, сейчас пришло в голову. Мы с вами всегда запускаем три теста, да? Пока нет проблем, у нас запуск длится чуть меньше 8 секунд. Но будет у нас пять тестов, будет 10 тестов. Ну, достаточно глупо, особенно если вы новый тест пишете, смотреть первые 10, которые у вас готовы. А поэтому, давайте вам ещё одну команду покажу, которая вам позволит запускать не просто отдельный файл, но определённый тест в этом файле. То есть, мы с вами запускали `pytest test_locators.py`, и вот здесь вот можно через два двоеточия передать ещё и имя теста, которое мы хотим запустить. В нашем случае это `test_locator_data_test_id`. Запускаем. И как мы видим, у нас уже только один тест запускается, не все три, а только один. Очень удобно.

Ну что, давайте ещё, может быть, пару локаторов посмотрим и будем потихонечку закругляться. Уже много полезного я вам сегодня рассказал. Давайте вот этот вот, например, локатор посмотрим. Я помню, что там был элемент с атрибутом `alt`, и по-моему, это как раз вот наш домашний логотип. Сейчас мы посмотрим. Навожу, нахожу, и вот, да, посмотрите, у него есть `alt="логотип"`. И что я сейчас попробую сделать? Я проверю то, что когда мы нажимаем на этот логотип, мы переходим именно на домашнюю страницу. И давайте мы, например, со страницы какой-нибудь аудиокниги, сейчас потом нажмём на вот этот баннер, найдём его по локатору `get_by_alt_text` и проверим, что у нас откроется `litres.ru`. Вот так вот. А так, как он у нас называется? Сейчас я ещё раз посмотрю. `locators.py`. `alt_text`. Да, всё.

Поехали. Следующий тест пишем. `def test_locator_alt_text(page):`. `page`. `page`. Дальше мы откроем `page.go_to`, но сделаем это с другой страницы: "аудиокниги". "аудиокниги". Дальше найдём наш `get_by_alt_text`. Найдём наш логотип. Вот он. Мы прям его найдём, делаем ему `click`. И как посмотреть, что у нас открывается нужная страница? Делаем это опять же через `expect`. В скобочках `page`. И здесь у нас есть такая команда `to_have_url`. Соответственно, мы с вами попробуем проверить, что у нас открывается именно тот URL, который нам нужен: `https://www.litres.ru/`. Запускаем наш тест. Опять же, мы будем запускать не все тесты, которые у нас есть, а `locator_alt_text` в режиме `headed`. Запускаем. Один тест найден, открывается аудиокнига, нажимается, и всё падает. Да, на всякий случай, давайте проверим какой-нибудь другой URL сюда добавим, что у нас всё падает. Запустился, опять задумался, скорее всего, сейчас, да, выдал нам ошибку. Всё работает, всё отлично.

И по базовым локаторам я предлагаю на этом остановиться. Давайте я вам тоже то, что в прошлый раз мы чуть-чуть коснулись, это дополнительные локаторы. Да, их тоже тут много разных есть: CSS локаторы, есть XPath, есть React локаторы. Не будем касаться всех. Давайте остановимся на XPath, потому что, ну, бывает такое действительно, что у элементов нету никаких атрибутов, `id`-шники никакие не прописали, и вот ну никак его не найти, только XPath ему можно прописать. А выберем какой-нибудь элемент, и вот, давайте, футер окунем и попробуем на YouTube перейти, например, с сайта ЛитРес. Вот как до этого элемента достучаться. Сейчас я посмотрю, что у него тут с атрибутами. Опять же, вы всегда можете по-разному стучаться к одним и тем же элементам. А, ну, здесь можно попробовать уже известный нам `by_role`, например, сделать, да, либо `by_title`. Но мы попробуем через XPath. Да, соответственно, вот нам нужно найти вот эту ссылочку и на неё кликнуть. Попробуем это сделать.

Напишем тест, заключительный на сегодня. `def test_locator_xpath(page):`. `page`. `page`. `page.go_to`. Давайте я снова скопирую и сразу начнём его искать. `page`. Давайте даже через `xpath` это сделаем. Просто что у нас есть ссылочка вот эта вот на YouTube. Просто я вам покажу, смотрите, какая проблема бывает с XPath-ами. То есть, у нас, чтобы обратиться к такому локатору, нужно просто сделать `locator` и здесь передать `xpath`. Сейчас так вот делается `xpath`. И можно делать прям глупо, да, то есть, прям взять в DevTools сделать `copy`, скопировать `xpath`, и у нас вылезет вот такая вот страшная штука. Как-то экранировать можно. В прошлый раз показывал, это сделать, оно, наверное, сработает, но сразу перестанет работать, как только у вас будет простое изменение, например, какой-нибудь новый баннер на сайте появится, вот эти вот `div`-ы поменяются местами, что-то исчезнет, что-то появится, и всё, тест сразу будет падать. Поэтому, если вы доходите до XPath-а, то старайтесь писать XPath таким образом, чтобы вот такие небольшие изменения они не сильно вас сбивали с толку и не ломали ваши тесты. А соответственно, у этого элемента есть определённые атрибуты, и можно попытаться идентифицировать вот эту вот ссылку там через тот же атрибут `title`, либо через атрибут вот это вот ссылки. Ну, в общем, как-то написать, чтобы какие-то простые изменения другого контента на сайте они на этот XPath не влияли.

Давайте я это запишу через через атрибут `title` равно, там, по-моему, "YouTube" был, да? Как он там был написан? Сейчас. Вот так вот: "YouTube". Вот так он должен по идее у нас работать. Так, кавычку. Всё, вообще не по плану пошло. Вот должно по идее сработать. То есть, XPath, ссылочка, у которой есть `title` с названием "YouTube". И что надо? Это сделать `visible`, да? Просто что она у нас отображается. Всё. Вот такой вот тест написали. Сейчас я попробую его отдельно запустить. `xpath`. Погнали. Нашёл один тест, открылось страница, и всё, он найден. Всё, по крайней мере, больше шансов, что такой XPath у вас проживёт немножко дольше, чем если бы вы писали через вот эти вот `div`-ы и так далее.

Всё, последнее, что мы можем сделать, это запустить все наши тесты. Давайте мы сделаем `pytest --headed`. Сейчас у нас пять тестов найдено. И пробегу, посмотрим, что они все отработают. Спасибо вам за активность на канале. Прямо первые два видео, которые я выложил, там много комментариев. Очень приятно, мотивирует меня продолжать. Продолжайте тоже это делать. И почти закончился наш тест, да? Мы видим, что пять из пяти тестов отработала. Всё у нас хорошо. Надеюсь, видео и урок был вам полезен. Подписывайтесь на канал и до встречи в следующих видео.