JavaScript и TypeScript для автоматизации тестирования

Интенсивный курс по освоению языков JS и TS с фокусом на создание автотестов. Программа охватывает синтаксис, асинхронность, типизацию и популярные инструменты автоматизации.

1. Основы современного JavaScript: синтаксис ES6+, работа с массивами и асинхронность

Основы современного JavaScript: синтаксис ES6+, работа с массивами и асинхронность

Добро пожаловать в курс JavaScript и TypeScript для автоматизации тестирования. Это первая статья, и мы начнем с фундамента, на котором строится вся современная веб-разработка и автоматизация.

Почему именно JavaScript (и его «старший брат» TypeScript)? Потому что это язык веба. Если вы пишете автотесты для браузера (UI) или тестируете API Node.js сервисов, знание внутренней кухни JS позволит вам писать стабильные, быстрые и поддерживаемые тесты. Современные инструменты, такие как Playwright, Cypress или WebdriverIO, требуют уверенного владения синтаксисом ES6+.

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

Переменные: прощай, var

В старом JavaScript для объявления переменных использовалось ключевое слово var. У него было много проблем, связанных с областью видимости, что часто приводило к трудноуловимым ошибкам. В современном стандарте мы используем let и const.

const (Константа)

Это ваш выбор по умолчанию. Если вы создаете переменную и не планируете переприсваивать ей значение (например, селектор кнопки или базовый URL), используйте const.

Важно понимать: const запрещает переприсваивание самой переменной, но если в переменной лежит объект или массив, их содержимое менять можно.

let (Переменная)

Используйте let только тогда, когда значение переменной действительно будет меняться в процессе выполнения кода. Например, счетчик в цикле или переменная, хранящая текущий статус теста.

Стрелочные функции (Arrow Functions)

Стрелочные функции сделали код лаконичнее и решили старую проблему с потерей контекста this (хотя в автотестах проблема this встречается реже, краткость очень важна).

Классическая функция:

Стрелочная функция:

Если тело функции состоит из одной строки, можно убрать фигурные скобки и слово return — возврат значения произойдет автоматически (неявный возврат):

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

Шаблонные строки (Template Literals)

Раньше для склеивания строк мы использовали оператор +. Это было неудобно, особенно при формировании сложных локаторов или сообщений об ошибках.

Теперь мы используем обратные кавычки (backticks) ` ` и конструкцию {elementId}"];

console.log(Поиск элемента с селектором: ${selector}); javascript const response = { id: 101, name: 'Test User', email: 'test@example.com', role: 'admin' };

// Старый способ // const id = response.id; // const email = response.email;

// Новый способ const { id, email } = response;

console.log(id, email); // 101, 'test@example.com' javascript const defaultUser = { name: 'User', password: '123', role: 'user' };

// Создаем админа на основе обычного юзера const adminUser = { ...defaultUser, role: 'admin' };

console.log(adminUser); // { name: 'User', password: '123', role: 'admin' } javascript const prices = [100, 200, 300]; const pricesWithTax = prices.map(price => price * 1.2); // [120, 240, 360] javascript const elements = [ { id: 1, visible: true }, { id: 2, visible: false }, { id: 3, visible: true } ];

const visibleElements = elements.filter(el => el.visible); // [{ id: 1, visible: true }, { id: 3, visible: true }] javascript const users = [{ name: 'Alice' }, { name: 'Bob' }]; const bob = users.find(user => user.name === 'Bob'); // { name: 'Bob' } javascript // Объявляем асинхронную функцию теста async function testLogin() { // Ждем, пока браузер откроет страницу. Код дальше не пойдет, пока это не случится. await browser.open('/login'); // Ждем, пока найдем элемент и введем текст await browser.type('#username', 'admin'); await browser.type('#password', '12345'); // Ждем клика await browser.click('#submit-btn'); // Ждем появления элемента на новой странице const welcomeMessage = await browser.getText('#welcome'); if (welcomeMessage === 'Hello, admin') { console.log('Test Passed'); } } javascript async function getData() { try { const response = await api.get('/users'); console.log(response.data); } catch (error) { console.error('Не удалось получить данные:', error); } } `

Заключение

Мы рассмотрели основные кирпичики современного JavaScript: * let и const для управления данными. * Стрелочные функции для краткости. * Шаблонные строки для динамического текста. * Методы массивов map, filter, find для работы с коллекциями. * async/await` для управления потоком выполнения в тестах.

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

2. Введение в TypeScript: статическая типизация, интерфейсы и настройка окружения

Введение в TypeScript: статическая типизация, интерфейсы и настройка окружения

В предыдущей статье мы разобрали фундамент современного JavaScript: переменные, стрелочные функции, асинхронность и методы массивов. Вы уже можете писать скрипты, которые работают. Но по мере того как ваш проект с автотестами будет расти, вы начнете сталкиваться с проблемами, характерными для динамической типизации.

Представьте ситуацию: вы написали тест, который ожидает, что функция вернет число (ID пользователя), а она возвращает строку. Или вы передали в функцию объект с опечаткой в названии свойства (usreName вместо userName). JavaScript узнает об этом только в момент выполнения кода, то есть когда тест уже упадет. Это трата времени.

Здесь на сцену выходит TypeScript.

Что такое TypeScript и зачем он нужен автоматизатору?

TypeScript (TS) — это надстройка над JavaScript (суперсет), разработанная Microsoft. Это означает, что любой валидный код на JS является валидным кодом на TS. Главное отличие — наличие статической типизации.

!Диаграмма Венна, демонстрирующая, что TypeScript включает в себя все возможности JavaScript плюс систему типов.

Для автоматизатора TypeScript решает три главные задачи:

  • Раннее обнаружение ошибок. Вы узнаете о том, что передали не тот тип данных, еще на этапе написания кода, а не при запуске теста.
  • Автодополнение (IntelliSense). IDE (например, VS Code) точно знает, какие поля есть у объекта и какие методы доступны, подсказывая их вам.
  • Читаемость и документация. Типы служат лучшей документацией. Глядя на сигнатуру функции, вы сразу понимаете, что она принимает и что возвращает.
  • Установка и настройка окружения

    Браузеры и Node.js не умеют исполнять TypeScript напрямую. Код на TS должен быть скомпилирован (транслирован) в JavaScript. Давайте настроим окружение.

    Шаг 1: Установка

    Предполагается, что Node.js у вас уже установлен. Создайте новую папку для проекта и инициализируйте его:

    Теперь установим TypeScript как зависимость для разработки:

    Также нам понадобится инструмент для удобного запуска TS-файлов без явной компиляции (удобно для тестов) — ts-node:

    Шаг 2: Конфигурация (tsconfig.json)

    Чтобы компилятор знал, как обрабатывать ваш код, нужен файл конфигурации. Создайте его командой:

    В корне проекта появится файл tsconfig.json. Вот базовые настройки, рекомендуемые для автоматизации:

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

    В TypeScript мы можем явно указывать, какой тип данных должна хранить переменная. Синтаксис прост: после названия переменной ставится двоеточие и тип.

    Базовые типы

    Если вы попытаетесь присвоить переменной timeout строку, TypeScript немедленно подчеркнет это как ошибку.

    Тип any (и почему его нужно избегать)

    Существует специальный тип any, который отключает проверку типов для переменной. Это по сути возвращение к обычному JavaScript.

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

    Функции

    В функциях мы типизируем аргументы и возвращаемое значение.

    Интерфейсы (Interfaces)

    Интерфейсы — это, пожалуй, самый мощный инструмент для автоматизатора. Они позволяют описать структуру объекта. Это критически важно при работе с API (описание Request/Response) или при создании Page Objects.

    Представьте, что API возвращает данные пользователя. В JS мы бы просто гадали, какие там поля. В TS мы создаем контракт.

    Если вы попытаетесь создать объект типа User и забудете поле email или укажете id как строку, компилятор не даст собрать проект.

    Readonly свойства

    Иногда нам нужно гарантировать, что поле объекта не будет изменено после создания. Например, id пользователя в базе данных обычно неизменен.

    Типизация в тестах: практический пример

    Давайте посмотрим, как это выглядит в реальном сценарии тестирования API. Допустим, мы тестируем создание пользователя.

    Без TypeScript мы бы писали так:

    Что такое userData? Что вернет функция? Непонятно. Приходится лезть в документацию API или код функции.

    С TypeScript:

    Теперь, когда вы будете вызывать createUser, редактор кода подскажет вам, что нужно передать объект с полями name и job. А результат выполнения функции будет автоматически восприниматься как UserResponse, и вы сможете обращаться к response.createdAt с уверенностью, что такое поле существует.

    Обратите внимание на Promise<UserResponse>. Так как функция асинхронная (async), она всегда возвращает Промис. В угловых скобках мы указываем, что именно вернет этот Промис, когда выполнится.

    Компиляция и запуск

    Как мы уже говорили, браузер не понимает TS. У нас есть два пути запуска кода:

  • Компиляция в JS.
  • Выполните команду npx tsc. В папке dist (или той, что вы указали в outDir) появятся .js файлы. Их можно запускать через node dist/main.js.

  • Запуск через ts-node.
  • Для разработки и запуска тестов удобнее использовать ts-node, который компилирует код «на лету» в памяти.

    Заключение

    TypeScript требует немного больше времени на старте для написания типов и интерфейсов. Однако это время окупается сторицей, когда: * Вы возвращаетесь к коду через месяц и сразу понимаете структуры данных. * IDE подсказывает вам методы и свойства, избавляя от опечаток. * Рефакторинг кода становится безопасным: изменили интерфейс — компилятор сразу подсветил все места, где нужно внести правки.

    В следующей статье мы углубимся в объектно-ориентированное программирование (ООП) в TypeScript и научимся применять классы для построения надежной архитектуры автотестов.

    3. Модульное тестирование: использование Jest, структура тестов и утверждения

    Модульное тестирование: использование Jest, структура тестов и утверждения

    В предыдущих статьях мы заложили прочный фундамент: изучили современный синтаксис JavaScript, разобрались с асинхронностью и настроили строгую типизацию с помощью TypeScript. Теперь пришло время поговорить о качестве кода. Как убедиться, что написанная вами функция работает именно так, как задумано, и не сломается после внесения изменений через месяц?

    Ответ прост: модульное тестирование (Unit Testing).

    В этой статье мы познакомимся с самым популярным фреймворком для тестирования в экосистеме JavaScript — Jest. Мы научимся настраивать его для работы с TypeScript, разберем структуру тестов и изучим основные утверждения (assertions).

    Что такое модульное тестирование?

    Модульное тестирование — это процесс проверки отдельных, самых мелких частей программы (модулей, функций, классов) в изоляции от остальной системы.

    Для автоматизатора это может показаться странным: «Зачем мне юнит-тесты, если я пишу E2E (End-to-End) тесты для браузера?». Вот несколько причин:

  • Тестирование вспомогательных функций. В любом фреймворке автоматизации (Playwright, WebdriverIO) вы пишете хелперы: генераторы случайных данных, парсеры строк, форматтеры дат. Их нужно тестировать отдельно.
  • Page Objects. Логику методов Page Object можно и нужно покрывать юнит-тестами.
  • Скорость. Юнит-тесты выполняются за миллисекунды. E2E тесты — за минуты. Если ошибка в логике вычислений, лучше узнать о ней мгновенно.
  • Почему Jest?

    Jest — это стандарт де-факто в современном мире JavaScript. Он разработан Facebook и обладает рядом преимуществ:

    * Zero config. Работает «из коробки» для большинства JS-проектов. * Встроенные утверждения. Вам не нужно подключать отдельные библиотеки (как Chai или Mocha). * Скорость. Jest запускает тесты параллельно. * Отличная поддержка TypeScript. Через препроцессор ts-jest.

    Настройка окружения

    Предполагается, что у вас уже есть проект с настроенным TypeScript из прошлой лекции. Давайте добавим в него Jest.

    Установим сам фреймворк, типы для него и инструмент для работы с TS:

    Теперь создадим конфигурационный файл для Jest. Благодаря ts-jest это делается одной командой:

    В корне проекта появится файл jest.config.js. Этого достаточно для старта.

    Последний штрих — добавим скрипт запуска в package.json:

    Структура теста: Pattern AAA

    Хороший тест всегда следует паттерну AAA: Arrange, Act, Assert.

    !Визуализация структуры идеального модульного теста.

  • Arrange (Подготовка): Мы подготавливаем данные, создаем объекты или моки.
  • Act (Действие): Мы вызываем метод, который хотим протестировать.
  • Assert (Проверка): Мы проверяем, соответствует ли результат ожиданиям.
  • Синтаксис Jest: describe и test

    Тесты в Jest организуются с помощью двух основных функций:

    * describe(name, fn): Группирует несколько тестов в один блок (например, «Тесты для калькулятора»). * test(name, fn) или it(name, fn): Непосредственно сам тест-кейс.

    Рассмотрим пример. Допустим, у нас есть файл src/utils.ts с простой функцией:

    Создадим файл теста src/utils.test.ts (Jest автоматически находит файлы с суффиксами .test.ts или .spec.ts):

    Запустите тест командой npm test. Вы увидите зеленый отчет об успешном прохождении.

    Утверждения (Matchers)

    Сердце теста — это функция expect(). Она принимает полученное значение и предоставляет набор методов («матчеров») для проверки.

    Основные матчеры

    * .toBe(value): Строгое равенство (аналог ===). Подходит для примитивов (числа, строки, булевы значения). * .toEqual(value): Глубокое равенство. Критически важно для сравнения объектов и массивов.

    Пример разницы:

    Работа с правдивостью

    Иногда нужно проверить не конкретное значение, а его «суть»:

    * .toBeNull(): соответствует только null. * .toBeUndefined(): соответствует только undefined. * .toBeDefined(): противоположность undefined. * .toBeTruthy(): соответствует всему, что if считает true. * .toBeFalsy(): соответствует всему, что if считает false.

    Строки и массивы

    Для строк часто используются регулярные выражения, а для массивов — проверка наличия элемента.

    Тестирование асинхронного кода

    В первой статье мы много говорили о Promise и async/await. Jest отлично умеет с ними работать. Есть два основных способа тестирования асинхронных функций.

    Предположим, у нас есть функция, имитирующая запрос к API:

    Способ 1: Async / Await (Рекомендуемый)

    Этот способ делает тест чистым и понятным, похожим на синхронный код.

    Способ 2: Работа с .resolves / .rejects

    Можно использовать специальные матчеры для промисов:

    Если вы ожидаете ошибку, используйте .rejects.

    Жизненный цикл тестов (Setup & Teardown)

    Часто перед тестами нужно выполнить настройку (например, создать запись в базе данных), а после — очистку (удалить запись). Для этого существуют хуки:

    * beforeAll(() => { ... }): Выполняется один раз перед всеми тестами в файле (или в блоке describe). * afterAll(() => { ... }): Выполняется один раз после всех тестов. * beforeEach(() => { ... }): Выполняется перед каждым тестом. * afterEach(() => { ... }): Выполняется после каждого теста.

    Пример использования:

    В данном примере «Connecting» выполнится 1 раз, а «Clearing tables» — 2 раза (перед каждым тестом).

    Заключение

    Мы разобрали основы модульного тестирования с Jest. Теперь вы умеете:

  • Настраивать Jest в TypeScript проекте.
  • Структурировать тесты с помощью describe и test.
  • Использовать матчеры toBe, toEqual и другие для проверки результатов.
  • Тестировать асинхронные функции.
  • Управлять состоянием через хуки жизненного цикла.
  • Эти навыки критически важны. Даже если ваша основная задача — писать UI-тесты, понимание того, как тестируется код на низком уровне, позволит вам лучше понимать разработчиков и писать более стабильные вспомогательные инструменты.

    В следующей статье мы перейдем к «тяжелой артиллерии» и начнем знакомство с инструментами для E2E тестирования.

    4. UI-автоматизация: взаимодействие с браузером и локаторы на примере Playwright

    UI-автоматизация: взаимодействие с браузером и локаторы на примере Playwright

    Мы прошли долгий путь: изучили синтаксис современного JavaScript, внедрили строгую типизацию с TypeScript и научились писать модульные тесты на Jest. Теперь у нас есть все необходимые инструменты, чтобы перейти к самому зрелищному этапу — UI-автоматизации.

    В этой статье мы познакомимся с Playwright — современным инструментом для E2E (End-to-End) тестирования от Microsoft. Мы разберем, как он управляет браузером, почему «старые» способы поиска элементов больше не работают и как писать тесты, которые не падают от малейшего изменения верстки.

    Почему Playwright?

    Раньше стандартом индустрии был Selenium WebDriver. Он был революционным для своего времени, но современный веб изменился. Сайты стали динамичными (React, Vue, Angular), асинхронными и сложными. Selenium часто не успевал за скоростью отрисовки интерфейса, что приводило к «хрупким» тестам (flaky tests).

    Playwright решает эти проблемы архитектурно:

  • Скорость: Он общается с браузером напрямую через протокол отладчика (DevTools Protocol), а не через HTTP-прослойку.
  • Автоматическое ожидание (Auto-wait): Playwright сам ждет, пока элемент станет видимым, активным и кликабельным, прежде чем выполнить действие. Вам больше не нужно писать sleep(5000).
  • Полная изоляция: Каждый тест запускается в чистом контексте, что исключает влияние тестов друг на друга.
  • TypeScript из коробки: Не нужно сложных настроек, типы работают сразу.
  • Архитектура: Браузер, Контекст и Страница

    Чтобы эффективно писать тесты, нужно понимать иерархию объектов в Playwright. Она состоит из трех уровней.

    !Иерархия объектов Playwright: один браузер может содержать несколько изолированных контекстов, а контекст — несколько страниц.

    Browser (Браузер)

    Это экземпляр браузера (Chromium, Firefox или WebKit). Запуск браузера — дорогая операция, занимающая время. Обычно он запускается один раз на все тесты.

    Browser Context (Контекст)

    Это аналог режима «Инкогнито». Контексты создаются мгновенно и полностью изолированы друг от друга. У них свои куки, кэш и локальное хранилище. Если в одном тесте вы залогинились как Админ, а во втором нужен Гость — разные контексты решат эту задачу параллельно.

    Page (Страница)

    Это отдельная вкладка в браузере внутри контекста. Именно с объектом Page мы взаимодействуем чаще всего: ищем элементы, кликаем и проверяем текст.

    Установка и первый тест

    В папке вашего проекта выполните команду:

    Выберите TypeScript при установке. Playwright создаст структуру папок и файл playwright.config.ts. Пример простейшего теста в папке tests/example.spec.ts:

    Обратите внимание: аргумент { page } — это фикстура. Playwright сам создает для каждого теста новый контекст и новую страницу, и передает её в тест.

    Локаторы: как найти элемент и не сойти с ума

    Самая частая причина падения UI-тестов — изменение верстки. Разработчик поменял div на section или добавил класс, и ваш селектор div > span.btn-primary перестал работать.

    Playwright предлагает философию «Тестируй так, как это делает пользователь». Пользователь не ищет кнопку по CSS-классу или XPath. Он ищет её по тексту «Войти» или по тому, что это поле для ввода «Email».

    Рекомендуемые локаторы

    Используйте эти методы в приоритетном порядке:

  • page.getByRole() — самый надежный локатор. Опирается на семантику HTML и доступность (Accessibility).
  • page.getByText() — поиск по видимому тексту.
  • page.getByLabel() — идеально для форм, где есть <label for="...">.
  • page.getByPlaceholder() — поиск по атрибуту placeholder.
  • Локаторы, которых стоит избегать (по возможности)

    * page.locator('css selector') * page.locator('xpath')

    Используйте CSS и XPath только тогда, когда стандартные методы не справляются (например, у элемента нет текста и ролей, и разработчики не добавили data-testid).

    Цепочки локаторов (Chaining)

    Иногда нужно найти кнопку «Редактировать» именно в карточке конкретного товара. Для этого локаторы можно соединять.

    В этом примере мы сначала нашли все карточки товаров, отфильтровали ту, где есть текст "iPhone 15", и внутри неё кликнули кнопку.

    Взаимодействие с элементами (Actions)

    Playwright предоставляет методы для всех действий пользователя. Все они асинхронны, поэтому не забывайте await.

    * click() — клик мышкой. * fill() — ввод текста в поле (с предварительной очисткой). * check() / uncheck() — работа с чекбоксами и радио-кнопками. * selectOption() — выбор значения в выпадающем списке (<select>). * hover() — наведение курсора.

    Пример заполнения формы:

    Auto-waiting в действии

    Когда вы вызываете click(), Playwright делает множество проверок:

  • Элемент прикреплен к DOM?
  • Элемент видим?
  • Элемент стабилен (не двигается, анимация завершена)?
  • Элемент получает события (не перекрыт другим элементом)?
  • Если кнопка перекрыта спиннером загрузки, Playwright будет ждать (по умолчанию 30 секунд), пока спиннер исчезнет, и только потом кликнет. Это делает тесты невероятно стабильными.

    Утверждения (Assertions)

    В Jest мы использовали expect(value).toBe(expected). В Playwright есть расширенный набор матчеров специально для веба.

    Главная особенность утверждений Playwright — они тоже асинхронны и умеют ждать.

    Представьте, что после нажатия кнопки текст меняется с «Loading...» на «Done». Если использовать обычный Jest, тест упадет, так как проверка произойдет мгновенно. Playwright же будет опрашивать элемент снова и снова, пока условие не выполнится или не выйдет таймаут.

    Практический пример: Тест корзины

    Давайте соберем всё вместе в небольшом сценарии добавления товара в корзину.

    Заключение

    Playwright кардинально меняет подход к написанию автотестов. Вместо борьбы с таймаутами и сложными CSS-селекторами, вы фокусируетесь на поведении пользователя. Использование семантических локаторов (getByRole) делает ваши тесты устойчивыми к рефакторингу и одновременно проверяет доступность вашего приложения.

    В следующей статье мы разберем паттерн Page Object Model (POM) — стандарт архитектуры для крупных проектов, который позволит переиспользовать код и навести порядок в тестах.

    5. Архитектура тестового проекта: паттерн Page Object Model и отчетность

    Архитектура тестового проекта: паттерн Page Object Model и отчетность

    В предыдущей статье мы познакомились с Playwright и научились писать первые UI-тесты. Мы искали элементы, кликали по кнопкам и проверяли заголовки. Пока у вас 3-5 тестов, подход «найти локатор — кликнуть — проверить» работает отлично. Но представьте, что ваш проект вырос до 500 тестов.

    Вдруг разработчики меняют верстку кнопки «Войти» (например, меняют id или структуру HTML). Если вы писали локаторы прямо в теле тестов, вам придется открывать 50 файлов и править один и тот же селектор 50 раз. Это кошмар любого автоматизатора.

    Чтобы избежать этого хаоса, мы внедрим архитектурный паттерн Page Object Model (POM) и настроим профессиональную отчетность, чтобы результаты наших тестов были понятны всей команде.

    Проблема «Спагетти-кода» в тестах

    Давайте посмотрим на типичный тест новичка, написанный «в лоб»:

    В чем проблемы этого кода?

  • Дублирование. Локаторы #username и .submit-btn будут встречаться в десятках тестов (логин, смена пароля, проверка прав доступа).
  • Нечитаемость. Если селекторы станут сложными (например, div > form > span:nth-child(2)), понять суть теста будет невозможно.
  • Сложность поддержки. Любое изменение верстки ломает все тесты сразу.
  • Что такое Page Object Model (POM)?

    Page Object Model — это паттерн проектирования, при котором каждая страница веб-приложения (или ее значимая часть) описывается в виде отдельного класса.

    !Архитектурная схема разделения логики тестов и описания страниц

    Суть паттерна проста: * Тест отвечает за бизнес-логику (ЧТО мы тестируем: сценарий, данные, проверки). * Page Object отвечает за техническую реализацию (КАК мы это делаем: локаторы, методы взаимодействия с элементами).

    Если верстка меняется, мы правим код только в одном месте — в классе Page Object. Тесты при этом остаются неизменными.

    Реализация POM на TypeScript и Playwright

    Давайте перепишем наш пример с логином, используя этот подход. Мы создадим класс LoginPage.

    Шаг 1: Создание класса страницы

    Создайте папку pages в корне проекта, а в ней файл LoginPage.ts.

    Обратите внимание на типизацию. Мы используем типы Page и Locator из Playwright, чтобы TypeScript помогал нам с автодополнением.

    Шаг 2: Использование Page Object в тесте

    Теперь наш тест станет чистым и понятным, как инструкция на английском языке.

    Теперь, если разработчик изменит поле ввода пароля, мы зайдем в LoginPage.ts, поменяем одну строчку в конструкторе, и все 50 тестов, использующих логин, снова заработают.

    Компоненты: разделяй и властвуй

    Современные сайты состоят не только из уникальных страниц, но и из повторяющихся блоков: шапка (Header), подвал (Footer), боковое меню. Не стоит копировать локаторы меню в класс каждой страницы.

    Лучше выделить их в отдельные компоненты.

    Теперь этот компонент можно использовать внутри других страниц:

    В тесте это будет выглядеть так:

    Отчетность (Reporting)

    Вы написали идеальные тесты с использованием POM. Вы запустили их, и консоль показала: 5 passed, 1 failed. Но почему упал тест? На каком шаге? Как показать это менеджеру?

    Вам нужны отчеты.

    Встроенный HTML Reporter

    Playwright имеет великолепный встроенный генератор отчетов. Он включен по умолчанию в конфигурации. После прогона тестов просто выполните команду:

    Откроется веб-страница, где вы увидите: * Список всех тестов. * Время выполнения. * В случае ошибки: скриншот, видео (если включено) и точное место падения в коде. * Трейс (Trace Viewer) — пошаговая запись всего, что происходило, с возможностью «путешествовать во времени» и смотреть состояние DOM на каждом клике.

    Allure Report

    Если вы работаете в большой компании, скорее всего, стандартом является Allure. Это мощный инструмент, который строит красивые графики, хранит историю запусков и позволяет интегрироваться с системами управления задачами (Jira).

    Чтобы подключить Allure к Playwright:

  • Установите пакет:
  • Настройте playwright.config.ts:
  • Запустите тесты. Появится папка allure-results.
  • Сгенерируйте отчет (вам понадобится установленная утилита Allure Commandline):
  • В Allure вы можете использовать специальные аннотации прямо в коде тестов, чтобы сделать отчет информативнее:

    Лучшие практики архитектуры

    Подводя итог, сформулируем золотые правила архитектуры тестового проекта:

  • Никаких локаторов в тестах. Если вы видите page.locator('.btn') внутри файла .spec.ts — это технический долг. Выносите в Page Object.
  • Имена методов должны отражать бизнес-действие. Не называйте метод fillUsernameAndClickButton. Назовите его login.
  • Атомарность. Page Object не должен содержать проверок (assertions), если это возможно. Он должен возвращать данные или состояние, а тест должен решать, правильно это или нет. Исключение: проверки, ожидающие завершения анимации или загрузки страницы внутри метода действия.
  • Базовый класс. Создайте BasePage, в который вынесите общие методы (открытие URL, ожидание загрузки), и наследуйте от него остальные страницы.
  • Заключение

    Внедрение Page Object Model превращает набор разрозненных скриптов в стройную систему. Ваш код становится объектно-ориентированным, типизированным и легким в поддержке. А подключение отчетности делает результаты вашей работы прозрачными для всей команды.

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