Ответ
При написании селекторов для Selenium/Selenide я активно использую оси XPath, чтобы создавать устойчивые и точные локаторы, особенно когда у элементов нет уникальных id или class.
Наиболее часто используемые оси:
-
ancestorиancestor-or-self: Незаменимы для поиска общего контейнера. Например, чтобы найти строку таблицы (<tr>), в которой находится определенная ячейка.// Найти кнопку 'Delete' в той же строке, где есть текст 'Project Alpha' // $x("//td[text()='Project Alpha']/ancestor::tr//button[text()='Delete']") WebElement deleteBtn = driver.findElement( By.xpath("//td[.='Project Alpha']/ancestor::tr//button[.='Delete']") ); -
following-siblingиpreceding-sibling: Полезны для навигации по элементам одного уровня, например, по пунктам списка (<li>) или строкам.// В Selenide: кликнуть на чекбокс, который следует за лейблом с текстом 'Accept terms' $x("//label[contains(., 'Accept terms')]/following-sibling::input[@type='checkbox']") .click(); -
parent: Быстрый способ подняться на один уровень вверх, часто используется в комбинации с другими осями.// Найти родительский div элемента с ошибкой, чтобы получить его текст String errorContainerText = driver.findElement( By.xpath("//span[@class='error-message']/parent::div") ).getText(); -
descendant(неявно) иchild: Я предпочитаю использовать//(неявныйdescendant) для поиска вглубь, когда структура DOM может меняться, и/(явныйchild), когда путь должен быть строгим.// Неявный descendant (более гибкий): //div[@id='modal']//input // Явный child (более строгий): //form[@id='login']/div[1]/input -
Ось
attribute(сокращенно@): Используется постоянно для поиска по атрибутамid,class,data-testid,nameи т.д.
Практический совет по устойчивости: Я стараюсь избегать построения длинных цепочек осей, которые сильно зависят от структуры HTML. Вместо этого комбинирую оси с предикатами (условиями в квадратных скобках) для поиска ближайшего устойчивого контейнера.
// ПЛОХО: Хрупкий локатор, зависит от точной позиции div
// //label[.='Email']/../../div[2]/input
// ЛУЧШЕ: Ищем input, который находится после label, но внутри общего родителя form
// //form//label[.='Email']/following::input[1]
// ИЛИ ЕЩЕ ЛУЧШЕ: Использовать data-атрибуты, если это возможно
// //input[@data-qa='email-input']
В Selenide, благодаря лаконичному синтаксису, это выглядит еще читаемее: $("//td[.='Project Alpha']/ancestor::tr").$(".delete-btn").click();.