Работа с вложенными таблицами

Работа с таблицами является одной из ключевых задач при автоматизированном тестировании веб-приложений. Вложенные таблицы — таблицы, содержащие внутри себя другие таблицы — требуют особого подхода при локализации элементов и организации тестов в Protractor.

Особенности вложенных таблиц

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

  • Иерархичность DOM: вложенные таблицы находятся внутри <td> родительской таблицы, что усложняет локаторы.
  • Динамическое отображение: дочерние таблицы могут рендериться только при раскрытии строки родительской таблицы.
  • Неоднородная структура: количество строк и столбцов в дочерних таблицах может отличаться.

Локаторы для вложенных таблиц

Protractor использует WebDriver API, что позволяет применять гибкие локаторы:

  • By.css и By.xpath для точного указания пути к элементам.
  • element и element.all для работы с одиночными и множественными элементами.

Пример выбора строки родительской таблицы и вложенной ячейки:

const parentRow = element.all(by.css('table.parent tbody tr')).get(2);
const childCell = parentRow.element(by.css('td table.child tr td')).getText();

В этом примере:

  • table.parent tbody tr выбирает все строки родительской таблицы.
  • get(2) возвращает третью строку (индексация с нуля).
  • td table.child tr td находит первую ячейку дочерней таблицы внутри выбранной строки.

Итерация по вложенным таблицам

Для динамических вложенных таблиц часто требуется пройтись по всем строкам родительской таблицы и извлечь данные из дочерних таблиц. Это реализуется через Promises и async/await:

const parentRows = element.all(by.css('table.parent tbody tr'));

for (let i = 0; i < await parentRows.count(); i++) {
    const row = parentRows.get(i);
    const childRows = row.all(by.css('td table.child tbody tr'));
    
    for (let j = 0; j < await childRows.count(); j++) {
        const childRow = childRows.get(j);
        const cellText = await childRow.element(by.css('td')).getText();
        console.log(`Родительская строка ${i}, дочерняя строка ${j}: ${cellText}`);
    }
}

Ключевые моменты:

  • Использование await предотвращает ошибки, связанные с асинхронностью.
  • Локаторы комбинируются через element и all, чтобы поддерживать вложенную структуру.
  • Каждая дочерняя таблица обрабатывается в контексте родительской строки, что исключает пересечение с другими вложенными таблицами.

Работа с динамическими таблицами

Если дочерняя таблица появляется только после клика по родительской строке:

const parentRow = element.all(by.css('table.parent tbody tr')).get(1);
await parentRow.click(); // раскрываем дочернюю таблицу

const childTable = parentRow.element(by.css('td table.child'));
const childCells = childTable.all(by.css('tr td'));

for (let i = 0; i < await childCells.count(); i++) {
    console.log(await childCells.get(i).getText());
}

Особенности:

  • Необходимо убедиться, что дочерняя таблица отображена, иначе локатор вернет пустой набор.
  • Для явного ожидания можно использовать browser.wait:
const EC = protractor.ExpectedConditions;
await browser.wait(EC.visibilityOf(childTable), 5000);

Проверка содержимого вложенных таблиц

Для проверки данных можно использовать стандартные Jasmine-ассерты или Chai:

const expectedValues = ['John', 'Doe', '25'];
const childCells = parentRow.element.all(by.css('td table.child tr td'));

for (let i = 0; i < expectedValues.length; i++) {
    expect(await childCells.get(i).getText()).toEqual(expectedValues[i]);
}

Это обеспечивает точное соответствие данных и поддерживает читаемость тестов.

Советы по оптимизации

  1. Минимизировать глубину локаторов — избегать длинных XPath без необходимости.
  2. Использовать CSS-классы — предпочтительнее атрибутов или индексов, так как структура таблицы может меняться.
  3. Асинхронная обработка — всегда использовать async/await при работе с .getText(), .count() и другими WebDriver-промисами.
  4. Разделение логики — лучше писать отдельные функции для работы с родительскими и дочерними таблицами, чтобы тесты оставались читаемыми.

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