Коротко. Playwright ходит в RX как пользователь и ловит то, что мониторинг сервисов не видит. Селекторы берите из codegen по ролям и подписям, а не по CSS-классам. Входите один раз и сохраняйте состояние. Первая проверка: загрузка веб-клиента, список заданий, отсутствие ошибок в консоли.

После обновления RX кто-то должен войти в систему, открыть документ, создать задачу и убедиться, что всё работает. Обычно это администратор в два часа ночи, и обычно он проверяет не всё. Автотест на Playwright делает то же самое за минуту, одинаково каждый раз, и его можно запускать не только после обновления, но и каждое утро. Это первая часть серии: от установки до первой проверки, которой уже можно пользоваться. Дальше будут данные для тестов, проверка сценариев с несколькими пользователями и запуск по расписанию с уведомлениями.

Симптом

  • После обновления часть функций сломана, и узнают об этом пользователи утром.
  • Проверка после работ это список в голове одного человека.
  • Мониторинг показывает, что сервисы живы, а войти в систему нельзя: сломан единый вход или веб-клиент не загружается.

Почему так

Мониторинг сервисов отвечает на вопрос «процессы работают?», а пользователю важно «могу ли я сделать свою работу?». Между этими вопросами пропасть: сертификат, прокси, кэш браузера, прикладная разработка, права. Закрыть её можно только проверкой, которая ходит в систему как пользователь.

Playwright управляет настоящим браузером (Chromium, Firefox, WebKit), сам ждёт появления элементов и записывает трассу каждого шага: снимки экрана, сетевые запросы, консоль. Для одностраничного веб-клиента RX, где всё появляется асинхронно, это важнее, чем для обычных сайтов.

Установка

Нужен Node.js. В пустой папке:

npm init playwright@latest

Установщик спросит язык (выбирайте TypeScript), папку для тестов и скачает браузеры. В закрытом контуре браузеры скачиваются заранее на машине с интернетом и переносятся, путь к ним задаётся переменной PLAYWRIGHT_BROWSERS_PATH. Второй вариант: не скачивать браузер совсем, а использовать уже установленный Google Chrome или Microsoft Edge, указав в настройках channel: 'chrome' или channel: 'msedge'. Так тест к тому же идёт в том же браузере, что у пользователей.

В playwright.config.ts задаём адрес системы и общие настройки:

import { defineConfig } from '@playwright/test';

export default defineConfig({
  testDir: './tests',
  timeout: 60_000,
  retries: 1,
  use: {
    baseURL: process.env.RX_URL ?? 'https://rx.example.local/Client/',
    trace: 'retain-on-failure',
    screenshot: 'only-on-failure',
    ignoreHTTPSErrors: false,
  },
  projects: [
    { name: 'setup', testMatch: /auth\.setup\.ts/ },
    { name: 'smoke', dependencies: ['setup'], use: { storageState: '.auth/user.json' } },
  ],
});

ignoreHTTPSErrors: false оставляем намеренно: если сертификат системы истёк или цепочка неполная, тест должен упасть, как упал бы браузер пользователя.

Селекторы: не угадывать, а записать

Самая частая ошибка первых автотестов: селекторы по CSS-классам вида .x-btn-123. Классы в веб-клиенте генерируются при сборке и меняются с версиями, тест ломается при каждом обновлении.

Playwright предлагает селекторы по тому, что видит пользователь: роль элемента и его подпись. Не придумывайте их, а запишите:

npx playwright codegen https://rx.example.local/Client/

Откроется браузер и окно с кодом. Пройдите вход и нужное действие руками, Playwright запишет шаги с селекторами вида getByRole('button', { name: 'Войти' }). Подписи в вашей системе могут отличаться от примеров ниже, поэтому берите их из записи.

Вход один раз

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

Файл tests/auth.setup.ts:

import { test as setup, expect } from '@playwright/test';

setup('вход', async ({ page }) => {
  await page.goto('');
  // подписи полей и кнопки возьмите из записи codegen
  await page.getByLabel('Имя пользователя').fill(process.env.RX_USER!);
  await page.getByLabel('Пароль').fill(process.env.RX_PASSWORD!);
  await page.getByRole('button', { name: 'Войти' }).click();
  // признак успешного входа: элемент, который есть только у вошедшего пользователя
  await expect(page.getByRole('navigation')).toBeVisible();
  await page.context().storageState({ path: '.auth/user.json' });
});

Учётная запись для тестов отдельная, с паролем, без второго фактора и с минимальными правами: только то, что нужно проверкам. Пароль передаётся переменной окружения, в коде его нет. Папку .auth добавляем в .gitignore.

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

Первая проверка

Файл tests/smoke.spec.ts. Проверяем то, без чего пользователи не работают: веб-клиент загрузился, открывается список заданий, работает поиск.

import { test, expect } from '@playwright/test';

test('веб-клиент загружается и показывает задания', async ({ page }) => {
  const started = Date.now();
  await page.goto('');
  await expect(page.getByRole('navigation')).toBeVisible();
  // название раздела возьмите из записи codegen
  await page.getByText('Входящие').first().click();
  await expect(page.getByRole('grid')).toBeVisible();
  console.log(`загрузка до списка заданий: ${Date.now() - started} мс`);
});

test('нет ошибок в консоли при загрузке', async ({ page }) => {
  const errors: string[] = [];
  page.on('console', (m) => { if (m.type() === 'error') errors.push(m.text()); });
  await page.goto('');
  await expect(page.getByRole('navigation')).toBeVisible();
  expect(errors, errors.join('\n')).toHaveLength(0);
});

Запуск:

RX_URL=https://rx.example.local/Client/ RX_USER=autotest RX_PASSWORD='…' npx playwright test

Если тест упал, отчёт покажет шаг, снимок экрана и полную трассу: npx playwright show-report. По трассе видно сетевые запросы и ответы, это часто сразу показывает причину: 502 от прокси, 401 от поставщика входа, скрипт веб-клиента не загрузился.

Ожидания

Playwright сам ждёт, пока элемент появится и станет доступен для действия. Поэтому:

  • не пишите waitForTimeout(5000): тест станет медленным и всё равно будет иногда падать;
  • ждите результат, который видит пользователь: expect(...).toBeVisible(), toHaveText(), toHaveCount();
  • если действие запускает долгую операцию, увеличьте таймаут конкретной проверки, а не всего теста.

Что дальше

Во второй части: тестовые данные (создать документ и задачу в начале, убрать за собой в конце), проверки с двумя пользователями (отправил задачу, исполнитель получил) и устойчивость к медленной системе. В третьей: запуск по расписанию из CI, уведомление при падении и время шагов как метрика в мониторинге.

Чего не делать

  • Не использовать CSS-классы веб-клиента в селекторах. Они меняются с версиями.
  • Не запускать тесты под учёткой администратора. Тест должен видеть систему глазами пользователя и не иметь возможности что-то сломать.
  • Не отключать проверку сертификата. Истёкший сертификат это как раз то, что тест должен поймать.
  • Не делать один огромный тест на всё. Короткие независимые тесты показывают, что именно сломалось.