E2E тестирование TypeScript: Полный гайд для разработчика

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Ваше приложение работает? Докажите это 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();
  });
});

Блоги

Рекомендаций по книгам