Ваше приложение работает? Докажите это E2E тестами на TypeScript
Вы когда-нибудь слышали фразу: «У нас всё работает, мы же тестировали вручную»? И тут же, на следующий день, падает продакшен? Знакомо, правда? Ручное тестирование — это как гадание на кофейной гуще. Вы никогда не знаете, нажмете ли вы на ту самую кнопку, которая сломает весь флоу пользователя.
E2E (End-to-End) тестирование на TypeScript — это ваш цифровой QA-инженер, который не спит, не пьет кофе и проверяет каждую кнопку 24/7. В этой статье мы не просто расскажем, что такое E2E тесты. Мы напишем их вместе: от установки до боевого CI/CD пайплайна. Пристегнитесь, будет жарко и полезно!
Почему именно TypeScript для E2E тестов?
Казалось бы, E2E тесты можно писать на чем угодно: JavaScript, Python, даже на Java (но давайте не будем о грустном). Однако TypeScript дает вам суперсилу — статическую типизацию. Вы больше не будете ловить баги типа Cannot read property 'click' of undefined в рантайме. Ваша IDE подсветит ошибку еще до запуска теста.
Плюс, если ваше приложение написано на TypeScript (а сегодня так делают 90% команд), то писать тесты на том же языке — логично. Вы используете те же типы, те же интерфейсы, те же утилиты. Это снижает когнитивную нагрузку и ускоряет разработку.
Выбираем инструмент: Playwright vs Cypress
Рынок E2E инструментов для TypeScript невелик, но есть два явных лидера: Cypress и Playwright. Мы будем использовать Playwright. Почему?
- Мультибраузерность из коробки: Chromium, Firefox, WebKit (Safari). Cypress пока не умеет нормально работать с Safari.
- Скорость: Playwright работает через протокол DevTools, а не через прокси-сервер, что делает тесты быстрее.
- Работа с iframe и вкладками: Playwright не накладывает ограничений на работу с несколькими вкладками или кросс-доменными iframe.
- TypeScript first: Playwright написан на TypeScript и полностью поддерживает его.
Но если вы новичок, Cypress может быть проще для старта. Выбор за вами. Мы же идем в бой с Playwright.
Установка и настройка проекта
Создадим новый проект и установим Playwright. Откройте терминал и выполните:
mkdir e2e-typescript-guide
cd e2e-typescript-guide
npm init -y
npm install @playwright/test
npx playwright install
Последняя команда скачает браузеры (Chromium, Firefox, WebKit). Это займет пару минут — идите за кофе.
Создадим файл playwright.config.ts в корне проекта:
import { defineConfig } from '@playwright/test';
export default defineConfig({
testDir: './tests',
fullyParallel: true,
retries: 1,
use: {
baseURL: 'http://localhost:3000',
trace: 'on-first-retry',
},
projects: [
{
name: 'chromium',
use: { browserName: 'chromium' },
},
],
});
Мы настроили базовый URL (замените на адрес вашего приложения), включили трассировку для отладки и указали, что тесты будут лежать в папке tests.
Пишем первый E2E тест на TypeScript
Создайте папку tests и файл login.spec.ts. Предположим, у нас есть простая форма логина.
import { test, expect } from '@playwright/test';
test.describe('Авторизация пользователя', () => {
test('Успешный вход с корректными данными', async ({ page }) => {
// Шаг 1: Открываем страницу логина
await page.goto('/login');
// Шаг 2: Заполняем поля
await page.getByLabel('Email').fill('user@example.com');
await page.getByLabel('Пароль').fill('securepassword123');
// Шаг 3: Нажимаем кнопку "Войти"
await page.getByRole('button', { name: 'Войти' }).click();
// Шаг 4: Ждем редиректа на дашборд
await page.waitForURL('/dashboard');
// Шаг 5: Проверяем, что отображается приветствие
await expect(page.getByText('Добро пожаловать, User!')).toBeVisible();
});
test('Отображение ошибки при неверном пароле', async ({ page }) => {
await page.goto('/login');
await page.getByLabel('Email').fill('user@example.com');
await page.getByLabel('Пароль').fill('wrongpassword');
await page.getByRole('button', { name: 'Войти' }).click();
// Проверяем сообщение об ошибке
await expect(page.getByText('Неверный email или пароль')).toBeVisible();
});
});
Обратите внимание: мы используем getByRole и getByLabel — это лучшие практики. Не используйте CSS-селекторы или XPath без крайней необходимости. Playwright рекомендует доступные локаторы (Accessibility-first подход).
Продвинутые техники: Фикстуры и Page Object Model
Когда тестов становится много, копировать один и тот же код авторизации в каждый тест — боль. Используем Page Object Model (POM) и фикстуры Playwright.
Создадим папку pages и файл LoginPage.ts:
import { Page, Locator } from '@playwright/test';
export class LoginPage {
readonly page: Page;
readonly emailInput: Locator;
readonly passwordInput: Locator;
readonly submitButton: Locator;
readonly errorMessage: Locator;
constructor(page: Page) {
this.page = page;
this.emailInput = page.getByLabel('Email');
this.passwordInput = page.getByLabel('Пароль');
this.submitButton = page.getByRole('button', { name: 'Войти' });
this.errorMessage = page.getByText('Неверный email или пароль');
}
async goto() {
await this.page.goto('/login');
}
async login(email: string, password: string) {
await this.emailInput.fill(email);
await this.passwordInput.fill(password);
await this.submitButton.click();
}
}
Теперь перепишем тест с использованием POM:
import { test, expect } from '@playwright/test';
import { LoginPage } from '../pages/LoginPage';
test.describe('Авторизация (POM)', () => {
test('Успешный вход', async ({ page }) => {
const loginPage = new LoginPage(page);
await loginPage.goto();
await loginPage.login('user@example.com', 'securepassword123');
await expect(page).toHaveURL('/dashboard');
});
test('Ошибка при неверных данных', async ({ page }) => {
const loginPage = new LoginPage(page);
await loginPage.goto();
await loginPage.login('user@example.com', 'wrongpassword');
await expect(loginPage.errorMessage).toBeVisible();
});
});