Promise.all для параллельных действий

Playwright — это мощный инструмент для автоматизации браузерных тестов, который позволяет разработчикам и тестировщикам проводить проверку веб-приложений с помощью JavaScript. Одной из важнейших возможностей, которые предоставляет Playwright, является способность выполнять параллельные действия с использованием Promise.all. Эта функция позволяет одновременно запускать несколько асинхронных операций, что значительно ускоряет процесс тестирования и делает код более эффективным.

Основы Promise.all

Метод Promise.all принимает массив или объект с несколькими промисами и возвращает один промис, который разрешается, когда все переданные промисы будут завершены. Если один из промисов в массиве отклоняется, то отклоняется и сам промис, возвращаемый Promise.all. Важно помнить, что порядок выполнения промисов не гарантирован, однако результат работы будет возвращён в том же порядке, в каком промисы были переданы.

Применение в Playwright

Playwright использует промисы для асинхронных операций, таких как взаимодействие с браузером, получение информации о состоянии элементов, выполнение навигации и другие действия. В большинстве случаев, когда необходимо выполнить несколько независимых операций, можно использовать Promise.all для их параллельного выполнения, что ускоряет выполнение тестов.

Пример 1. Параллельные запросы на странице

Предположим, что требуется выполнить несколько запросов на странице, например, получить текст с нескольких элементов одновременно. Это можно сделать с помощью Promise.all:

const { chromium } = require('playwright');

(async () => {
  const browser = await chromium.launch();
  const page = await browser.newPage();

  await page.goto('https://example.com');

  // Ожидаем параллельно получения текста с нескольких элементов
  const [title, description, footer] = await Promise.all([
    page.locator('h1').innerText(),
    page.locator('.description').innerText(),
    page.locator('.footer').innerText()
  ]);

  console.log(title, description, footer);

  await browser.close();
})();

В этом примере три асинхронных операции — извлечение текста с трёх элементов страницы — выполняются параллельно, а результат возвращается в том порядке, в котором они были переданы в Promise.all.

Параллельное взаимодействие с элементами

Когда необходимо взаимодействовать с несколькими элементами на странице (например, кликать по нескольким кнопкам или заполнять формы), также можно использовать Promise.all. Это помогает избежать длительных задержек, связанных с последовательным выполнением каждого действия.

Пример 2. Параллельный клик по кнопкам

const { chromium } = require('playwright');

(async () => {
  const browser = await chromium.launch();
  const page = await browser.newPage();

  await page.goto('https://example.com');

  // Ожидаем параллельно клика по нескольким кнопкам
  await Promise.all([
    page.locator('#button1').click(),
    page.locator('#button2').click(),
    page.locator('#button3').click()
  ]);

  await browser.close();
})();

В этом примере, все три кнопки будут нажаты одновременно, что значительно ускоряет выполнение теста.

Использование Promise.all для работы с несколькими страницами

Если тесты требуют работы с несколькими вкладками или окнами браузера одновременно, Promise.all может помочь ускорить выполнение тестов. Playwright позволяет управлять несколькими страницами одновременно, и каждый промис, связанный с действиями на разных страницах, можно передать в Promise.all.

Пример 3. Параллельная работа с несколькими страницами

const { chromium } = require('playwright');

(async () => {
  const browser = await chromium.launch();
  const page1 = await browser.newPage();
  const page2 = await browser.newPage();

  await Promise.all([
    page1.goto('https://example.com'),
    page2.goto('https://example.org')
  ]);

  // Ожидаем параллельно получения заголовков с обеих страниц
  const [title1, title2] = await Promise.all([
    page1.title(),
    page2.title()
  ]);

  console.log(title1, title2);

  await browser.close();
})();

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

Обработка ошибок в Promise.all

Одной из особенностей использования Promise.all является то, что если один из промисов отклоняется, весь промис, возвращаемый Promise.all, также отклоняется. Поэтому важно учитывать возможность возникновения ошибок при выполнении параллельных операций.

Пример 4. Обработка ошибок с использованием Promise.all

const { chromium } = require('playwright');

(async () => {
  const browser = await chromium.launch();
  const page = await browser.newPage();

  await page.goto('https://example.com');

  try {
    // Параллельное выполнение запросов с обработкой ошибок
    const [title, nonExistentElement] = await Promise.all([
      page.title(),
      page.locator('#non-existent').innerText() // Этот элемент не существует
    ]);
    console.log(title);
  } catch (error) {
    console.error('Ошибка:', error);
  }

  await browser.close();
})();

В этом примере, если элемент с id #non-existent не существует, ошибка будет перехвачена в блоке catch, и программа продолжит выполнение без остановки.

Параллельные действия с ожиданием

В некоторых случаях необходимо не только выполнять действия параллельно, но и ожидать завершения нескольких операций, прежде чем переходить к следующему шагу. Для этого можно использовать Promise.all в сочетании с методами ожидания, такими как page.waitForSelector или page.waitForResponse.

Пример 5. Параллельное ожидание элементов

const { chromium } = require('playwright');

(async () => {
  const browser = await chromium.launch();
  const page = await browser.newPage();

  await page.goto('https://example.com');

  // Ожидаем параллельно появления элементов на странице
  await Promise.all([
    page.waitForSelector('.header'),
    page.waitForSelector('.footer'),
    page.waitForSelector('.main-content')
  ]);

  console.log('Все элементы появились на странице.');

  await browser.close();
})();

Здесь мы ожидаем появления трёх элементов на странице параллельно, что делает процесс ожидания более быстрым, чем последовательное использование waitForSelector для каждого элемента.

Производительность и использование Promise.all

При использовании Promise.all важно понимать, что параллельное выполнение задач повышает нагрузку на браузер, особенно если операций слишком много. В таких случаях рекомендуется тщательно анализировать, сколько операций можно выполнять одновременно, чтобы не перегрузить систему. В некоторых случаях может быть полезно разбить задачи на более мелкие группы и использовать их параллельно в несколько этапов.

Заключение

Использование Promise.all в Playwright позволяет эффективно выполнять несколько асинхронных операций параллельно, значительно ускоряя тестирование. Эта техника полезна как для взаимодействия с элементами на странице, так и для работы с несколькими вкладками или страницами. Важно учитывать обработку ошибок и возможности оптимизации при выполнении большого количества параллельных действий, чтобы избежать перегрузки системы.