Generic типы для переиспользуемых компонентов

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

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

Почему важны generic типы?

Основной задачей generic типов является создание кода, который может работать с различными типами данных, не ограничивая себя конкретными значениями или типами. В контексте тестирования с Playwright это имеет несколько значительных преимуществ:

  1. Универсальность: одна функция или класс может работать с различными типами данных, не требуя переписывания или дублирования кода.
  2. Переиспользуемость: компоненты тестов можно легко адаптировать к новым сценариям без необходимости изменения логики.
  3. Типизация: использование generic типов помогает строго типизировать функции и классы, что снижает вероятность ошибок, связанных с неверно переданными типами данных.

Основы работы с generic типами в TypeScript

Типы в TypeScript могут быть параметризированы через угловые скобки <>. Например, если есть класс, который работает с каким-то типом данных, можно сделать его generic, чтобы он мог работать с любым типом. Пример:

class PageComponent<T> {
  private data: T;

  constructor(data: T) {
    this.data = data;
  }

  getData(): T {
    return this.data;
  }
}

В этом примере класс PageComponent может работать с любым типом данных, переданным через параметр T. Это позволяет использовать этот класс для различных типов страниц, например:

const userPage = new PageComponent<UserData>({ name: 'John', age: 30 });
const productPage = new PageComponent<ProductData>({ id: 1, name: 'Product A' });

Применение generic типов в тестах с Playwright

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

Пример использования generic типов для создания универсального класса для работы с элементами:

import { Page, Locator } from 'playwright';

class Element<T> {
  private page: Page;
  private locator: Locator;
  private data: T;

  constructor(page: Page, locator: Locator, data: T) {
    this.page = page;
    this.locator = locator;
    this.data = data;
  }

  async click(): Promise<void> {
    await this.locator.click();
  }

  async typeText(): Promise<void> {
    await this.locator.fill(this.data as unknown as string);
  }

  async getText(): Promise<string> {
    return await this.locator.textContent();
  }
}

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

const button = new Element<boolean>(page, page.locator('button#submit'), true);
const inputField = new Element<string>(page, page.locator('input#name'), 'John Doe');

Создание переиспользуемых компонентов для различных типов страниц

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

Пример: создание универсального класса для работы с формами.

class Form<T> {
  private page: Page;
  private locators: { [key: string]: Locator };
  private formData: T;

  constructor(page: Page, locators: { [key: string]: Locator }, formData: T) {
    this.page = page;
    this.locators = locators;
    this.formData = formData;
  }

  async fillForm(): Promise<void> {
    for (const field in this.locators) {
      if (this.formData[field]) {
        await this.locators[field].fill(this.formData[field] as unknown as string);
      }
    }
  }

  async submit(): Promise<void> {
    await this.locators.submitButton.click();
  }
}

Теперь, для создания формы с любыми данными, можно использовать этот класс:

const loginForm = new Form<LoginFormData>(page, {
  username: page.locator('input#username'),
  password: page.locator('input#password'),
  submitButton: page.locator('button#submit'),
}, { username: 'john_doe', password: 'password123' });

await loginForm.fillForm();
await loginForm.submit();

Типизация и безопасность

Использование generic типов значительно повышает типовую безопасность кода, что позволяет избежать ошибок, таких как неверное использование методов или полей объекта. В приведенных выше примерах классы Element и Form строго типизированы, что гарантирует правильность передачи данных и взаимодействия с элементами страницы. TypeScript проверяет, что передаваемые типы данных соответствуют ожидаемым, и сообщает об ошибках на этапе компиляции, а не в процессе выполнения тестов.

Например, если мы попытаемся передать неверный тип данных в форму:

const loginForm = new Form<LoginFormData>(page, {
  username: page.locator('input#username'),
  password: page.locator('input#password'),
  submitButton: page.locator('button#submit'),
}, { username: 'john_doe', password: 123 });  // Ошибка, password должно быть строкой

TypeScript сразу выдаст ошибку, сообщая о несоответствии типа данных, что помогает избежать багов.

Преимущества использования generic типов в тестах

  1. Гибкость: возможности использования generic типов позволяют тестировать компоненты с различными типами данных, что повышает универсальность кода.
  2. Чистота кода: использование одного компонента для различных типов данных минимизирует повторяющийся код, облегчая поддержку и развитие тестов.
  3. Типовая безопасность: строгая типизация помогает избежать ошибок при написании тестов, что особенно важно при сложных интерфейсах и сценариях.

Заключение

Generic типы в TypeScript открывают большие возможности для улучшения тестов в Playwright, предоставляя механизмы для создания универсальных и переиспользуемых компонентов. Это позволяет улучшить не только поддержку кода, но и повысить его читаемость, обеспечивая безопасность типов и сокращая вероятность ошибок.