Навигация по DOM-дереву, оси, функции и примеры для QA
XPath (XML Path Language) — это язык для навигации по элементам XML/HTML документа. В тестировании XPath используется для поиска элементов в Selenium, Playwright и других инструментах.
| Выражение | Описание |
|---|---|
/html | Корневой элемент |
//div | Все элементы div в документе |
. | Текущий узел |
.. | Родительский узел |
@attr | Атрибут элемента |
text() | Текстовый узел |
| Тип | Обозначение | Пример |
|---|---|---|
| Element | tagname | div, button |
| Attribute | @name | //div[@id='foo'] |
| Text | text() | //button/text() |
| Comment | comment() | //comment() |
| Node | node() | Любой узел |
| Processing Instruction | processing-instruction() | XML инструкции |
// Текст кнопки //button/text() → "Войти" // Элемент по тексту //button[text()="Войти"] → <button>Войти</button> // Вход по текстовому содержимому //*[contains(text(), "Войти")]
Оси определяют направление движения по DOM-дереву относительно текущего узла.
self::
Текущий узел
parent::
Родительский узел
child::
Дочерние элементы
ancestor::
Все предки (вверх)
descendant::
Все потомки (вниз)
following-sibling::
Следующие соседи
preceding-sibling::
Предыдущие соседи
following::
Все после (в документе)
preceding::
Все до (в документе)
ancestor-or-self::
Предки + текущий
descendant-or-self::
Потомки + текущий
namespace::
Пространство имён
| Полная форма | Сокращение |
|---|---|
child::* | * |
descendant::* | // |
self::node() | . |
parent::node() | .. |
attribute::* | @* |
// 1. Следующий сосед (following-sibling) //input[@id='username']/following-sibling::button // Результат: <button type="submit">Войти</button> // 2. Родитель (parent) //input[@id='username']/parent::form // Результат: <form id="login">...</form> // 3. Первый следующий сосед //input[@id='username']/following-sibling::input[1] // Результат: <input id="password"> // 4. Все предки (ancestor) //button[@type='submit']/ancestor::html // Результат: <html>...</html> // 5. Дочерние элементы (child) //form[@id='login']/child::* // Результат: [input#username, input#password, button]
Предикаты — это условия в квадратных скобках [], которые фильтруют узлы.
// Первый элемент списка //li[1] // Результат: <li>Первый</li> // Последний элемент //li[last()] // Результат: <li>Последний</li> // Предпоследний //li[last()-1] // Результат: <li>Третий</li> // Первые 2 элемента //li[position()<=2] // Результат: [<li>Первый</li>, <li>Второй</li>]
// По id (уникальный) //div[@id='header'] // Результат: <div id="header">Шапка</div> // По class (точное совпадение) //div[@class='login-form active'] // Результат: <div class="login-form active">...</div> // class содержит 'login' //div[contains(@class, 'login')] // Результат: <div class="login-form active">...</div> // Несколько атрибутов (AND) //input[@type='text' and @name='username'] // Результат: <input type="text" name="username"> // Элемент с атрибутом disabled //button[@disabled] // Результат: <button type="submit" disabled>Войти</button> // class НЕ содержит 'hidden' //div[not(contains(@class, 'hidden'))] // Результат: [<div id="header">, <div class="login-form active">]
// Точное совпадение текста //a[text()='Главная'] // Результат: <a href="/">Главная</a> // Текст содержит подстроку //a[contains(text(), 'услуг')] // Результат: <a>Подробнее о услугах</a> // Текст начинается с //a[starts-with(text(), 'О')] // Результат: <a href="/about">О компании</a> // Любой элемент с текстом 'Отправить' //*[text()='Отправить'] // Результат: <button>Отправить</button> // Ссылка с текстом содержащим 'Подробнее' //a[contains(text(), 'Подробнее')] // Результат: <a>Подробнее о услугах</a>
contains(str, substr)
Содержит подстроку
starts-with(str, prefix)
Начинается с
ends-with(str, suffix)
Заканчивается на
substring(str, pos, len)
Подстрока
normalize-space(str)
Убирает лишние пробелы
string-length(str)
Длина строки
concat(str1, str2, ...)
Конкатенация
translate(str, from, to)
Замена символов
count(node-set)
Количество узлов
sum(node-set)
Сумма значений
position()
Позиция в наборе
last()
Последняя позиция
boolean(value)
Приведение к bool
not(expr)
Отрицание
true()
Истина
false()
Ложь
name(node?)
Имя тега
local-name(node?)
Локальное имя
namespace-uri(node?)
URI пространства имён
// Прямой дочерний элемент //form[@id='login']/div // Любой дочерний input //form[@id='login']/input // Все потомки input //form[@id='login']//input // Первый дочерний div //form[@id='login']/div[1]
// Родитель по оси //input[@id='user']/parent::div // То же через .. //input[@id='user']/../div // Два уровня вверх //input[@id='user']/../../ // Корневой элемент //input[@id='user']/ancestor::form
// Следующий сосед (после lastName идёт email) //input[@id='lastName']/following-sibling::input // Предыдущий сосед (перед lastName идёт firstName) //input[@id='lastName']/preceding-sibling::input // Первый следующий input //input[@id='firstName']/following-sibling::input[1] // Все следующие соседи //input[@id='firstName']/following-sibling::*
// Точное совпадение //button[text()='Отправить'] // Текст содержит //button[contains(text(), 'Отправ')] // Ссылка с текстом //a[text()='Читать далее']
// Все поля ввода в форме //form[@id='registration']//input // Input по name //input[@name='email'] // Обязательные поля //input[@required] // Поле по placeholder //input[@placeholder='Введите email'] // Кнопка submit в форме //form[@id='login']//button[@type='submit']
// Заголовки таблицы //table[@id='data']/thead//th // Строки таблицы //table[@id='data']/tbody/tr // Ячейка по координатам [row, col] //table//tr[2]/td[3] // Строка по содержимому //table//tr[td[contains(text(), 'Иванов')]]
// Элемент с динамическим id //div[starts-with(@id, 'dropdown-')] // Меню с вложенными пунктами //ul[@class='menu']//li[contains(@class, 'active')] // Скрытый элемент //div[@style='display: none;' and @class='tooltip'] //disabled кнопка //button[@disabled and contains(text(), 'Загрузка')]
// AND - оба условия //input[@type='text' and @required] // OR - хотя бы одно //input[@name='phone' or @name='mobile'] // NOT - отрицание //button[not(@disabled)] // Сложное условие //div[@class='card' and contains(@id, 'product') and position() <= 5]
Примечание: XPath 2.0+ поддерживается не всеми браузерами и инструментами. Selenium WebDriver использует XPath 1.0. Для Playwright доступны некоторые функции XPath 2.0+.
| Функция | Описание | XPath |
|---|---|---|
| lower-case() | Нижний регистр | 2.0+ |
| upper-case() | Верхний регистр | 2.0+ |
| matches() | Регулярное выражение | 2.0+ |
| replace() | Замена по regex | 2.0+ |
| tokenize() | Разбиение строки | 2.0+ |
| avg() | Среднее значение | 2.0+ |
| min(), max() | Мин/макс | 2.0+ |
id, data-testid, data-qa// для гибкости//[@data-qa='submit-btn'] лучше длинного DOM-путиПорядок поиска локаторов (по приоритету):
1. data-testid / data-qa → 2. id → 3. name → 4. CSS selector → 5. XPath