Типизация кастомных функций

Puppeteer — это библиотека для работы с браузером Google Chrome или Chromium с помощью API для автоматизации. В процессе тестирования с использованием Puppeteer часто возникает необходимость в создании кастомных функций для выполнения специфических действий на веб-страницах. Типизация этих функций с помощью TypeScript позволяет обеспечить большую предсказуемость кода, улучшить автодополнение и минимизировать количество ошибок.

Зачем нужна типизация в Puppeteer?

Типизация кода, особенно при использовании таких библиотек, как Puppeteer, позволяет избежать множества проблем:

  • Управление типами данных. Работа с различными типами данных в DOM, асинхронные операции и сложные объекты становятся гораздо удобнее при строгой типизации.
  • Автодополнение и рефакторинг. При использовании TypeScript редакторы кода могут предложить автодополнение и облегчить рефакторинг, позволяя быстрее и с меньшими ошибками менять структуру кода.
  • Обнаружение ошибок на ранних этапах. Типизация позволяет быстрее выявлять ошибки на стадии разработки, сокращая время, затраченное на тестирование и отладку.

Основы типизации в Puppeteer

Для работы с Puppeteer в TypeScript необходимо установить соответствующие типы:

npm install --save puppeteer
npm install --save-dev @types/puppeteer

После этого можно использовать типы, предоставляемые библиотекой, для работы с основными объектами, такими как Page, ElementHandle, Browser, и другие. Например, при взаимодействии с веб-страницей можно указать типы для переменных, которые содержат объекты Page или ElementHandle:

import puppeteer, { Page, Browser } from &

async function example() {
  const browser: Browser = await puppeteer.launch();
  const page: Page = await browser.newPage();
  
  await page.goto('https://example.com');
  
  // Действия с элементами страницы
  const element = await page.$('h1');
  if (element) {
    const text = await element.evaluate(el => el.textContent);
    console.log(text);
  }

  await browser.close();
}

Создание кастомных функций

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

Пример кастомной функции без типизации:

async function clickButton(page, selector) {
  await page.click(selector);
  await page.waitForSelector(selector, { visible: true });
}

Пример с типизацией:

async function clickButton(page: Page, selector: string): Promise<void> {
  await page.click(selector);
  await page.waitForSelector(selector, { visible: true });
}

Типизация позволяет явно указать типы входных параметров функции и возвращаемое значение. В данном примере page типизирован как Page, а selector как строка (string). Возвращаемое значение функции — Promise<void>, что означает, что функция асинхронная и не возвращает никакого значения.

Работа с элементами страницы

Часто в тестах приходится взаимодействовать с элементами DOM. Для этого Puppeteer предоставляет объект ElementHandle, который представляет собой ссылку на элемент на странице. Типизация ElementHandle помогает избежать ошибок при работе с DOM-элементами.

Пример:

async function getElementText(page: Page, selector: string): Promise<string | null> {
  const element: ElementHandle | null = await page.$(selector);
  if (element) {
    return element.evaluate(el => el.textContent);
  }
  return null;
}

Здесь element имеет тип ElementHandle | null, так как результат поиска может быть либо ссылкой на элемент, либо null, если элемент не найден.

Типизация объектов и параметров

В Puppeteer можно использовать дополнительные типы для более точной типизации объектов. Например, для работы с различными опциями, которые принимает метод page.goto, можно создать интерфейсы для их параметров.

Пример с типизацией параметров метода:

interface NavigationOptions {
  timeout: number;
  waitUntil: 'load' | 'domcontentloaded' | 'networkidle0' | 'networkidle2';
}

async function navigateToPage(page: Page, url: string, options: NavigationOptions): Promise<void> {
  await page.goto(url, { waitUntil: options.waitUntil, timeout: options.timeout });
}

В данном примере создается интерфейс NavigationOptions, который описывает параметры для метода goto. Это помогает гарантировать, что все передаваемые параметры будут корректными.

Работа с асинхронными функциями и типизация результатов

Большинство функций Puppeteer — асинхронные, и правильная типизация результатов асинхронных операций критична для стабильности и читаемости кода. Типизация Promise позволяет точно указать, что возвращается из асинхронных функций, и облегчает работу с результатами.

Пример работы с асинхронной функцией:

async function getPageTitle(page: Page): Promise<string> {
  const title: string = await page.title();
  return title;
}

Здесь тип Promise<string> указывает на то, что функция возвращает асинхронный результат, который будет строкой. Типизация таких функций помогает избежать ошибок, например, если случайно пытаются обработать результат как другой тип данных.

Обработка ошибок и типизация исключений

Работа с ошибками в Puppeteer также требует внимательной типизации. Например, если необходимо обрабатывать исключения, выбрасываемые при взаимодействии с элементами на странице, можно использовать типы для обработки ошибок.

Пример:

async function getElementTextWithErrorHandling(page: Page, selector: string): Promise<string | null> {
  try {
    const element: ElementHandle | null = await page.$(selector);
    if (element) {
      return await element.evaluate(el => el.textContent);
    }
    return null;
  } catch (error) {
    console.error('Error fetching element text:', error);
    return null;
  }
}

Здесь используется конструкция try-catch для перехвата возможных исключений, что важно для стабильности работы тестов. Тип возвращаемого значения функции по-прежнему остается string | null, что помогает избежать ошибок при обработке результата.

Типизация кастомных классов

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

Пример кастомного класса:

class PuppeteerHelper {
  private page: Page;

  constructor(page: Page) {
    this.page = page;
  }

  async clickElement(selector: string): Promise<void> {
    await this.page.click(selector);
    await this.page.waitForSelector(selector, { visible: true });
  }

  async getElementText(selector: string): Promise<string | null> {
    const element: ElementHandle | null = await this.page.$(selector);
    if (element) {
      return await element.evaluate(el => el.textContent);
    }
    return null;
  }
}

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

Итоговые рекомендации

  • Типизация в Puppeteer с использованием TypeScript позволяет создавать более стабильные и читаемые тесты.
  • Явное указание типов входных данных и возвращаемых значений помогает избежать множества ошибок, связанных с несовпадением типов.
  • Правильная типизация асинхронных функций и объектов, таких как Page и ElementHandle, значительно улучшает поддержку и развитие проекта.