Использование generic типов в тестах с Playwright позволяет создать гибкие и переиспользуемые компоненты, которые можно применить в различных ситуациях и с разными данными. В контексте тестирования веб-приложений, это особенно важно для создания универсальных компонентов, которые могут работать с разными типами страниц, элементов и данных.
Generic типы позволяют задать параметры типов, которые будут использоваться в различных частях приложения или теста, что делает код более универсальным, удобным для повторного использования и легче поддающимся изменениям в будущем.
Основной задачей generic типов является создание кода, который может работать с различными типами данных, не ограничивая себя конкретными значениями или типами. В контексте тестирования с Playwright это имеет несколько значительных преимуществ:
Типы в 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' });
Для создания универсальных тестов с 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 типы в TypeScript открывают большие возможности для улучшения тестов в Playwright, предоставляя механизмы для создания универсальных и переиспользуемых компонентов. Это позволяет улучшить не только поддержку кода, но и повысить его читаемость, обеспечивая безопасность типов и сокращая вероятность ошибок.