В этом руководстве вы изучите возможности Playwright, проведя сквозное (end-to-end) тестирование React-версии известного проекта TodoMVC.
Суть проекта TodoMVC – предоставление возможности разработчикам сравнить популярные JavaScript-фреймворки, анализируя код разных реализаций одного и того же приложения, созданных с помощью разных фреймворков. Не волнуйтесь, если вы не знакомы с React: основное внимание уделяется не его особенностям, а тестированию с помощью Playwright.
Подпишитесь на наш ТЕЛЕГРАМ КАНАЛ ПО АВТОМАТИЗАЦИИ ТЕСТИРОВАНИЯ
Содержание
- Шаг 1 – Настройка демо-проекта на React
- Шаг 2 – Настройка Playwright
- Шаг 3 – Написание первого теста
- Шаг 4 – Изучение UI-режима Playwright
- Шаг 5 – Обнаружение визуальных регрессий с помощью Playwright
- Заключение
Прежде чем продолжить, убедитесь, что у вас установлена последняя версия Node.js, предпочтительно версия LTS.
Примечание редакции: если вы еще не очень хорошо знакомы с Playwright, то возможно вам будет интересна статья Сквозное тестирование с Playwright: полное руководство.
Шаг 1 — Настройка демо-проекта на React
Начните с клонирования репозитория TodoMVC React на ваш локальный компьютер:
git clone https://github.com/betterstack-community/react-todo-mvc
После клонирования перейдите в директорию проекта и установите необходимые зависимости:
cd react-todo-mvc npm install
Этот шаг гарантирует, что все необходимые библиотеки и инструменты будут доступны для сервера разработки. После установки вы можете запустить сервер с помощью команды:
npm run dev
Сервер инициализируется и начнет прослушивать порт 8080. Вывод будет примерно таким:
> todomvc-react@1.0.0 dev > webpack serve --open --config webpack.dev.js <i> [webpack-dev-server] Проект запущен на: <i> [webpack-dev-server] Loopback: http://localhost:8080/ <i> [webpack-dev-server] В вашей сети (IPv4): http://192.168.0.189:8080/ . . .
Чтобы убедиться, что приложение работает правильно, откройте в браузере ссылку http://localhost:8080. Вы увидите интерфейс TodoMVC. Попробуйте добавить несколько задач, чтобы убедиться, что приложение функционирует.

Теперь с помощью Playwright вы создадите тесты, которые проверят основные функции приложения, такие как возможность добавлять задачи и управлять ими. Эти тесты продемонстрируют мощь и простоту Playwright для автоматизированного тестирования.
Перед тем как перейти к следующему шагу, остановите сервер разработки. Это можно сделать, нажав Ctrl-C в терминале, где запущен сервер. Сервер завершит работу корректно:
^C [webpack-dev-server] Gracefully shutting down. To force exit, press ^C again. Please wait...
Теперь, когда демо-проект настроен, можно интегрировать и настроить Playwright для написания и запуска сквозных тестов.
Шаг 2 — Настройка Playwright
В этом разделе вы установите Playwright в проект и настроите браузерные движки, необходимые для сквозного тестирования.
Начните с добавления Playwright в ваш проект. В корневой директории проекта выполните следующую команду:
npm init playwright@latest
Во время установки вам предложат настроить несколько параметров. Для нашего проекта выберите следующие параметры:
Initializing project in 'react-todo-mvc' ✔ Do you want to use TypeScript or JavaScript? · JavaScript ✔ Where to put your end-to-end tests? · tests ✔ Add a GitHub Actions workflow? (y/N) · false ✔ Install Playwright browsers (can be done manually via 'npx playwright install')? (Y/n) · true ✔ Install Playwright operating system dependencies (requires sudo / root - can be done manually via 'sudo npx playwright install-deps')? (y/N) · true
После установки вы увидите сообщение о завершении, показывающее, что Playwright и его зависимости теперь являются частью вашего проекта:
. . . Рекомендуем начать с команды: npx playwright test И ознакомьтесь со следующими файлами: - ./tests/example.spec.js - Пример сквозного теста - ./tests-examples/demo-todo-app.spec.js - Сквозные тесты для Todo App - ./playwright.config.js - Конфигурация тестов Playwright
Теперь давайте настроим Playwright для нашего тестирования. Откройте файл playwright.config.js
, который был создан во время установки, в вашем текстовом редакторе:
code playwright.config.js
Вот как выглядит playwright.config.js
:
// @ts-check const { defineConfig, devices } = require('@playwright/test'); module.exports = defineConfig({ testDir: './tests', fullyParallel: true, forbidOnly: !!process.env.CI, retries: process.env.CI ? 2 : 0, workers: process.env.CI ? 1 : undefined, reporter: 'html', use: { baseURL: 'http://127.0.0.1:8081', trace: 'on-first-retry', }, projects: [ { name: 'chromium', use: { ...devices['Desktop Chrome'] }, }, { name: 'firefox', use: { ...devices['Desktop Firefox'] }, }, { name: 'webkit', use: { ...devices['Desktop Safari'] }, }, ], webServer: { command: 'npm run playwright-server', url: 'http://127.0.0.1:8081', reuseExistingServer: !process.env.CI, }, });
Playwright предлагает множество параметров настройки тестов, но наиболее важные следующие:
- testDir указывает на директорию
./tests
, где будут находиться ваши тестовые файлы - projects определяет браузерные среды для тестирования (Chromium, Firefox, WebKit)
- webServer позволяет запускать локальный сервер перед тестированием, что полезно в разработке
- use.baseURL задает корневой URL вашего сервера, чтобы в тестах можно было использовать относительные пути вместо абсолютных
Прежде чем продолжить, создайте скрипт для Playwright в вашем файле package.json
:
"scripts": { "playwright-server": "webpack serve --port=8081 --config webpack.dev.js", },
Этот скрипт запустит сервер разработки на другом порту специально для тестирования Playwright.
Теперь Playwright настроен и готов к написанию первого тестового скрипта.
Шаг 3 — Написание первого теста
Playwright уже создал директорию tests
с примером теста. Для начала удалите этот пример:
rm tests/example.spec.js
Теперь создайте новый файл теста специально для вашего приложения To-do:
code tests/todo.spec.js
В этом новом файле вы напишете тест, который проверит начальное состояние приложения. Цель заключается в том, чтобы убедиться, что после загрузки нет ни одного элемента списка задач, а поле ввода находится в фокусе.
Напишите код для проверки этих условий:
const { test, expect } = require('@playwright/test'); test.describe('Initial state', () => { test('should be empty with focused input', async ({ page }) => { // Загрузка приложения TodoMVC await page.goto('/'); const todoList = page.getByTestId('todo-list'); // Убедитесь, что список не содержит элементов await expect(todoList).toBeEmpty(); const todoInput = page.getByTestId('text-input'); // Убедитесь, что поле ввода находится в фокусе await expect(todoInput).toBeFocused(); }); });
Функции Playwright test
и expect
помогают определить тест и выполнить проверки. Метод describe()
группирует связанные тесты и описывает, что охватывает данный набор тестов.
Внутри test.describe()
функция test()
определяет конкретный тест для проверки пустого состояния списка задач и фокуса на поле ввода. Тест переходит на приложение TodoMVC по адресу http://localhost:8081, а затем использует локатор getByTestId()
для поиска элементов по атрибуту data-testid
.

Затем с помощью функции expect()
мы проверяем, что список задач пуст, а поле ввода находится в фокусе.
Чтобы запустить тест, выполните следующую команду в терминале:
npx playwright test
Эта команда запускает сервер разработки и выполняет тесты в разных браузерах, как указано в playwright.config.js
. Все тесты должны пройти, что означает, что начальное состояние приложения работает как ожидалось во всех основных браузерах.
Output . . . Running 3 tests using 3 workers 3 passed (4.9s)
Чтобы открыть последний HTML-отчет, выполните:
npx playwright show-report
После подтверждения начального состояния приложения в следующем разделе вы протестируете более сложные взаимодействия с пользователем. Также вы изучите, как UI-режим Playwright помогает разбираться в этих взаимодействиях и отлаживать их.
Шаг 4 — Изучение UI-режима Playwright
В этой части вы создадите тест, чтобы убедиться, что задачи можно успешно добавлять в приложение, и воспользуетесь UI-режимом Playwright для визуализации и отладки выполнения теста.
Сначала обновите файл todo.spec.js
, добавив новый тест:
. . . const TODO_ITEMS = ['save the world', 'go back in time', 'learn C']; test.describe('New Todo', () => { test('should allow me to add todo items', async ({ page }) => { await page.goto('/'); // создаем локатор для нового списка задач const todoList = page.getByTestId('todo-list'); const todoInput = page.getByTestId('text-input'); // Добавляем первую задачу await todoInput.fill(TODO_ITEMS[0]); await todoInput.press('Enter'); // Убедитесь, что в списке только одна задача await expect(page.getByTestId('todo-item-label')).toHaveText([ TODO_ITEMS[0], ]); // Добавляем вторую задачу await todoInput.fill(TODO_ITEMS[1]); await todoInput.press('Enter'); // Убедитесь, что в списке две задачи await expect(page.getByTestId('todo-item-label')).toHaveText([ TODO_ITEMS[0], TODO_ITEMS[1], ]); }); });
Этот тест проверяет, что пользователи могут добавлять элементы в список дел, а список обновляется соответствующим образом.
Он начинается с загрузки приложения и выбора поля ввода для новых задач (с помощью его data-testid
). Затем выполняются следующие шаги:
- Первый элемент из массива
TODO_ITEMS
вводится в поле ввода и симулируется нажатие клавиши Enter. - Проверяется, что в списке задач содержится только этот первый элемент.
- Аналогично вводится второй элемент из
TODO_ITEMS
. - Снова проверяется список задач, чтобы убедиться, что теперь в нем содержатся как первый, так и второй элементы.
Чтобы наблюдать за выполнением теста, используйте команду:
npx playwright test -g 'New Todo' --ui
После выполнения этой команды откроется UI-режим Playwright. Этот инструмент позволяет визуально пошагово проходить тесты, исследовать DOM на разных этапах, просматривать временные шкалы выполнения тестов и анализировать действия, логи и ошибки в реальном времени. Этот режим значительно упрощает процесс отладки и улучшения тестов.

Когда UI-режим активирован, тест сначала будет на паузе. Чтобы продолжить выполнение, нажмите на значок “Play” рядом с заголовком теста. Тест должен успешно выполниться.

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

Вкладка Actions перечисляет все действия, выполненные в тесте, с указанием использованных локаторов и продолжительности выполнения. Вы можете щелкнуть по любому действию, чтобы увидеть изменения в приложении. Вкладки Before и After позволяют визуально сравнивать состояние приложения до и после выполнения действия.

Кроме того, можно исследовать DOM на любом этапе теста, нажав на значок внешней ссылки в верхнем правом углу снимка DOM. Откроется окно, в котором вы можете использовать DevTools браузера для более детального изучения и устранения проблем в скриптах.

Под снимком DOM доступны несколько вкладок с дополнительной информацией:
- Locator позволяет выбрать локаторы из снимка DOM и скопировать их в тестовый скрипт
- Source показывает код для выделенного действия
- Call отображает информацию о времени выполнения и деталях локатора
- Log содержит внутренние логи Playwright
- Errors показывает любые ошибки, возникшие во время теста
- Console и Network отображают логи приложения и сетевые запросы
- Attachments полезны для визуальных сравнений при регрессионном тестировании
Используя эти функции, вы сможете эффективно разбираться в поведении ваших тестов Playwright, чтобы убедиться, что они корректно проверяют функциональность приложения.
Шаг 5 — Обнаружение визуальных регрессий с помощью Playwright
Тестирование визуальных регрессий — это процесс проверки пользовательского интерфейса (UI) приложения на предмет визуальных изменений, которые могли возникнуть после внесения изменений в код. В ходе этого тестирования сравнивается текущий внешний вид элементов или страниц приложения с набором базовых изображений, созданных ранее, чтобы убедиться, что элементы интерфейса выглядят правильно в разных окружениях, браузерах или после изменений кода.
Playwright значительно упрощает проверку визуальных регрессий, следуя общему процессу:
- Сначала создается набор базовых изображений, представляющих ожидаемое состояние элементов интерфейса или страниц. Эти изображения служат отправной точкой для будущих сравнений.
- Затем во время выполнения теста захватывается текущее состояние интерфейса.
- Далее эти изображения сравниваются с эталоном. Любые расхождения отмечаются как возможные регрессии, которые могут быть вызваны изменениями в CSS или макете, обновлениями JavaScript или поведением браузера.
Playwright использует библиотеку pixelmatch для сравнения изображений на уровне пикселей. При первом выполнении теста визуальной регрессии создается и сохраняется эталонный снимок, который служит базовой точкой для последующих сравнений.
Чтобы увидеть это на практике, добавьте следующий тест в файл todo.spec.js
:
. . . test.describe('Visual comparison', () => { test('initial state', async ({ page }) => { await expect(page).toHaveScreenshot(); }); });
Этот тест использует метод toHaveScreenshot()
, чтобы захватить и сравнить текущее состояние страницы с ожидаемым базовым изображением.
Перед выполнением теста рекомендуется провести рефакторинг кода:
const { test, expect } = require('@playwright/test'); test.beforeEach(async ({ page }) => { await page.goto('/'); }); test.describe('Initial state', () => { test('should be empty with focused input', async ({ page }) => { const todoList = page.getByTestId('todo-list'); // дополнительные проверки }); }); const TODO_ITEMS = ['save the world', 'go back in time', 'learn C']; test.describe('New Todo', () => { test('should allow me to add todo items', async ({ page }) => { const todoList = page.getByTestId('todo-list'); // дополнительные проверки }); }); test.describe('Visual comparison', () => { test('initial state', async ({ page }) => { await expect(page).toHaveScreenshot(); }); });
Чтобы избежать повторения строки, которая переходит на корневой путь в каждом тесте, хук beforeEach()
следит за тем, чтобы страница загружалась перед выполнением каждого теста. Это помогает поддерживать чистую структуру тестов и обеспечивает соблюдение определённых условий перед выполнением каждого теста.
Теперь выполните набор тестов на визуальное сравнение, используя следующую команду:
npx playwright test -g 'Visual'
Изначально тест провалится из-за отсутствия базового изображения (так называемого “золотого файла”). Это ожидаемая ошибка, так как необходимо установить начальный базовый снимок.
npx playwright test -g 'Visual'
Output . . . 3 failed [chromium] › todo.spec.js:53:3 › Visual comparison › initial state ─────────────────────── [firefox] › todo.spec.js:53:3 › Visual comparison › initial state ──────────────────────── [webkit] › todo.spec.js:53:3 › Visual comparison › initial state ────────────────────────

После выполнения команды HTML-отчет откроется в браузере по умолчанию и покажет, что тесты не прошли во всех браузерах. Это произошло из-за отсутствия базовых изображений для сравнения. В отчете будет раздел «Скриншоты», где можно просмотреть сделанные снимки.

Эти начальные скриншоты сохраняются как эталонные изображения в вашем каталоге с тестами следующим образом:
tree tests
Output . . . tests ├── example.spec.js ├── todo.spec.js └── todo.spec.js-snapshots ├── Visual-comparison-initial-state-1-chromium-linux.png ├── Visual-comparison-initial-state-1-firefox-linux.png └── Visual-comparison-initial-state-1-webkit-linux.png
Имя каждого эталонного файла — это комбинация названия набора тестов, названия теста, браузера и платформы. Также можно задать имя скриншота вручную, используя аргумент в методе toHaveScreenshot()
:
await expect(page).toHaveScreenshot('todomvc-initial-state.png');
Это создаст следующие файлы:
todomvc-initial-state-chromium-linux.png todomvc-initial-state-firefox-linux.png todomvc-initial-state-webkit-linux.png
После создания базового скриншота все последующие тесты будут сравнивать текущее состояние интерфейса с этим эталоном.
Завершите работу HTML-отчета с помощью Ctrl-C, затем повторно выполните тест визуальной регрессии:
npx playwright test -g 'Visual'
Вы должны увидеть успешное выполнение тестов, что говорит об отсутствии визуальных расхождений:
Output . . . Running 3 tests using 3 workers 3 passed (5.3s) . . .
Чтобы продемонстрировать возможности отчетов Playwright для визуальной регрессии, внесите небольшое изменение в ваше приложение. Например, измените цвет заголовка в файле header.jsx
:
src/todo/components/header.jsx import { useCallback } from "react"; import { Input } from "./input"; import { ADD_ITEM } from "../constants"; export function Header({ dispatch }) { const addItem = useCallback((title) => dispatch({ type: ADD_ITEM, payload: { title } }), [dispatch]); return ( <header className="header" data-testid="header"> <h1 style={{color: "blue"}}>todos</h1> <Input onSubmit={addItem} label="New Todo Input" placeholder="What needs to be done?" /> </header> ); }
Это изменение меняет цвет текста заголовка на синий, имитируя типичное изменение в процессе разработки.
Выполните тест визуальной регрессии снова:
npx playwright test -g 'Visual'
На этот раз тест не пройдет, так как текущее состояние интерфейса будет отличаться от эталона:
Output . . . 3 failed [chromium] › todo.spec.js:53:3 › Visual comparison › initial state ─────────────────────── [firefox] › todo.spec.js:53:3 › Visual comparison › initial state ──────────────────────── [webkit] › todo.spec.js:53:3 › Visual comparison › initial state ────────────────────────
При провале теста отчет автоматически откроется в вашем браузере и предоставит детальные сведения.

Откройте любой элемент сбоя и прокрутите до раздела «Несоответствие изображений» для сравнения нового скриншота с эталоном. Вам будут предложены следующие вкладки:
- Diff выделяет затронутые элементы красным
- Actual показывает текущее изображение
- Expected показывает эталонное изображение
- Side by side размещает эталонное и текущее изображения рядом
- Slider позволяет интерактивно сравнивать с помощью ползунка
Тест может провалиться как из-за ошибок, так и из-за преднамеренных изменений. Если визуальные изменения не задумывались, нужно исправить причину и повторить тесты. Если изменения запланированы и должны стать новыми эталонами, обновите эталонные изображения командой:
npx playwright test -g 'Visual' --update-snapshots
Это перегенерирует базовые изображения, синхронизируя их с новым состоянием интерфейса:
Output . . . Running 3 tests using 3 workers [chromium] › todo.spec.js:53:3 › Visual comparison › initial state /home/ayo/dev/betterstack/demo/react-todo-mvc/tests/todo.spec.js-snapshots/Visual-comparison-initial-state-1-chromium-linux.png is re-generated, writing actual. [firefox] › todo.spec.js:53:3 › Visual comparison › initial state /home/ayo/dev/betterstack/demo/react-todo-mvc/tests/todo.spec.js-snapshots/Visual-comparison-initial-state-1-firefox-linux.png is re-generated, writing actual. [webkit] › todo.spec.js:53:3 › Visual comparison › initial state /home/ayo/dev/betterstack/demo/react-todo-mvc/tests/todo.spec.js-snapshots/Visual-comparison-initial-state-1-webkit-linux.png is re-generated, writing actual. 3 passed (5.1s) . . .
После обновления эталонных изображений тесты должны пройти, а обновленные скриншоты следует сохранить в системе контроля версий для использования в будущем.
Заключение
В этом руководстве мы разобрали, как создавать, выполнять и отлаживать сквозные тесты для React-приложения с помощью Playwright. Мы также рассмотрели важность регрессионного тестирования для поддержания визуальной целостности интерфейса и показали, как эффективно реализовать такие тесты с помощью Playwright.
Перевод статьи «Playwright End-to-End Testing: A Step-by-Step Guide».
Пингбэк: Поиск элементов в Playwright
Running 3 tests using 3 workers
1) [chromium] › tests/todo.spec.js:4:3 › Initial state › should be empty with focused input ──────
Error: page.goto: Protocol error (Page.navigate): Cannot navigate to invalid URL
Call log:
– navigating to “/”, waiting until “load”
4 | test(‘should be empty with focused input’, async ({ page }) => {
5 | // Загрузка приложения TodoMVC
> 6 | await page.goto(‘/’);
| ^
7 |
8 | const todoList = page.getByTestId(‘todo-list’);
9 |
at /Users/aleksJS/projects/hexlet-playwright/tests/todo.spec.js:6:16
Error Context: test-results/todo-Initial-state-should-be-empty-with-focused-input-chromium/error-context.md
2) [webkit] › tests/todo.spec.js:4:3 › Initial state › should be empty with focused input ────────
Error: browserContext.newPage: Protocol error (Page.overrideSetting): Unknown setting: FixedBackgroundsPaintRelativeToDocument
3) [firefox] › tests/todo.spec.js:4:3 › Initial state › should be empty with focused input ───────
Error: page.goto: Protocol error (Page.navigate): Invalid url: “/”
Call log:
– navigating to “/”, waiting until “load”
4 | test(‘should be empty with focused input’, async ({ page }) => {
5 | // Загрузка приложения TodoMVC
> 6 | await page.goto(‘/’);
| ^
7 |
8 | const todoList = page.getByTestId(‘todo-list’);
9 |
at /Users/aleksJS/projects/hexlet-playwright/tests/todo.spec.js:6:16
Error Context: test-results/todo-Initial-state-should-be-empty-with-focused-input-firefox/error-context.md
3 failed
[chromium] › tests/todo.spec.js:4:3 › Initial state › should be empty with focused input ───────
[firefox] › tests/todo.spec.js:4:3 › Initial state › should be empty with focused input ────────
[webkit] › tests/todo.spec.js:4:3 › Initial state › should be empty with focused input ─────────