Files
e2e_Pets/README.md

509 lines
24 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# e2e_Pets — End-to-End тесты портала учёта животных
Автоматизированное тестирование портала [vet.goznak.ru](https://vet.goznak.ru)
на базе **Playwright + pytest**.
---
## Содержание
- [Структура проекта](#структура-проекта)
- [Быстрый старт](#быстрый-старт)
- [Запуск тестов](#запуск-тестов)
- [Поддержка Стандартного и ГОСТ TLS](#поддержка-стандартного-и-гост-tls)
- [Создание нового теста](#создание-нового-теста)
- [Добавление генератора данных (ФЛК)](#добавление-генератора-данных-флк)
- [Добавление статического артефакта](#добавление-статического-артефакта)
- [Создание теста через визуальный рекордер (Codegen)](#создание-теста-через-визуальный-рекордер-codegen)
---
## Структура проекта
```
e2e_Pets/
├── .env # Учётные данные и настройки среды (не в git)
├── .env.example # Шаблон .env
├── conftest.py # Корневой conftest (CLI-флаги, HTML-отчёт и метаданные)
├── pytest.ini # Конфигурация pytest
├── requirements.txt # Зависимости
└── tests/
├── conftest.py # Глобальные фикстуры (browser_page, bp_state, _auto_browser)
├── testdata/
│ └── generators.py # Генераторы случайных данных по ФЛК
├── utils/
│ ├── tls.py # Детектор TLS и адаптивный менеджер браузеров
│ └── test_tls.py # Модульные тесты механизмов TLS
├── 01_vetdept/ # ЛК: Управление ветеринарии
│ ├── conftest.py # fixtures_dir, shared_state
│ ├── fixtures/ # Статические артефакты (фото, PDF)
│ └── BP01_navigation/ # Бизнес-процесс 01
│ └── test_bp01_navigation.py
└── 02_vetstation/ # ЛК: Ветеринарная станция
├── conftest.py
├── fixtures/
└── BP01_dashboard/ # Бизнес-процесс 01
└── test_bp01_dashboard.py
```
**Иерархия тестов:** Проект → ЛК (роль) → БП (`BP01_...`) → Шаги (`test_s01_...`, `test_s02_...`)
---
## Быстрый старт
### 1. Клонировать репозиторий
```powershell
git clone <url>
cd e2e_Pets
```
### 2. Создать виртуальное окружение и установить зависимости
```powershell
python -m venv venv
.\venv\Scripts\pip.exe install -r requirements.txt
.\venv\Scripts\playwright.exe install chromium
```
### 3. Настроить конфигурацию и учётные данные
```powershell
Copy-Item .env.example .env
# Открыть .env и указать BASE_URL, логины и пароли ролей
```
### 4. (Опционально) Настройка ГОСТ-браузера
Если целевой портал защищён ГОСТ TLS (ГОСТ Р 34.12-2015, 34.10-2012) или вы хотите тестировать ГОСТ-контур:
1. Скачайте архив **[Chromium-GOST](https://github.com/deemru/chromium-gost/releases)** (например, `chromium-gost-...-windows-amd64.zip`).
2. Распакуйте его в стандартный каталог `%LOCALAPPDATA%\Chromium-Gost` (или `C:\Program Files\Chromium-Gost`). Механизм тестов обнаружит его **автоматически**.
3. *Альтернатива:* если браузер распакован в произвольную папку, укажите путь в `.env`:
```ini
CHROMIUM_GOST_PATH=C:\MyPrograms\chromium-gost\chrome.exe
```
4. Убедитесь, что на компьютере установлен криптопровайдер с ГОСТ-алгоритмами (КриптоПро CSP, Security Code CSP / Континент-АП).
---
## Запуск тестов
> Все команды выполняются из корня проекта (`e2e_Pets/`).
> HTML-отчёт со статусом, типом TLS и запущенным браузером автоматически формируется в `html-report/report.html`.
---
### Автоматический запуск (рекомендуемый)
По умолчанию включён режим `--tls-mode=auto`. Система перед стартом проверяет сокет целевого `BASE_URL`:
- Если сайт доступен по **стандартному TLS** → автоматически запускается встроенный **Chromium Playwright**.
- Если сайт защищён **ГОСТ TLS** → автоматически запускается **Chromium-GOST**.
```powershell
# Запуск всех тестов в авто-режиме
.\venv\Scripts\pytest.exe
```
---
### Принудительный выбор режима TLS
Если вы хотите явно зафиксировать браузер в обход автоопределения:
```powershell
# Принудительно запустить через Chromium-GOST
.\venv\Scripts\pytest.exe --tls-mode=gost
# Принудительно запустить через стандартный Chromium Playwright
.\venv\Scripts\pytest.exe --tls-mode=standard
```
---
### Запуск тестов конкретного ЛК (роли)
```powershell
# ЛК Управление ветеринарии (все бизнес-процессы)
.\venv\Scripts\pytest.exe tests/01_vetdept/
# ЛК Ветеринарная станция (все бизнес-процессы)
.\venv\Scripts\pytest.exe tests/02_vetstation/
```
---
### Запуск конкретного бизнес-процесса или шага
```powershell
# Запуск BP01 в ЛК Управление ветеринарии
.\venv\Scripts\pytest.exe tests/01_vetdept/BP01_navigation/
# Запуск только шага 1 (Dashboard)
.\venv\Scripts\pytest.exe tests/01_vetdept/BP01_navigation/ -k "s01"
# Запуск шагов 1 и 2
.\venv\Scripts\pytest.exe tests/01_vetdept/BP01_navigation/ -k "s01 or s02"
```
---
### Полезные флаги запуска
| Флаг | Описание |
|---|---|
| `-v` | Подробный вывод с отображением docstring каждого шага |
| `--tls-mode=auto` | Режим TLS: `auto` (по умолчанию), `gost` или `standard` |
| `--ignore-https-errors` | Игнорировать ошибки сертификатов SSL (по умолчанию включено) |
| `-k "BP01"` | Фильтр тестов по подстроке в имени класса или метода |
| `--co -q` | Только показать список тестов без их выполнения |
| `--slowmo 500` | Замедление действий в браузере (в мс, по умолчанию 1000мс в `pytest.ini`) |
| `-o addopts=""` | Сбросить дефолтные флаги `pytest.ini` (например, для headless-запуска в CI) |
```powershell
# Просмотр дерева тестов без запуска браузера
.\venv\Scripts\pytest.exe --co -q
# Быстрый запуск без задержки slowmo
.\venv\Scripts\pytest.exe tests/01_vetdept/ --slowmo 0
# Запуск в режиме ГОСТ с подробным выводом
.\venv\Scripts\pytest.exe tests/01_vetdept/ --tls-mode=gost -v
```
---
## Поддержка Стандартного и ГОСТ TLS
В проект встроен универсальный механизм запуска, который автоматически определяет, какой протокол шифрования используется на целевом портале (`BASE_URL`), и выбирает подходящий браузер.
### Как работает автоопределение:
1. **Двухфазная детекция**:
- Выполняется тестовое SSL-рукопожатие сокета.
- Если сайт использует стандартные алгоритмы (TLS 1.2 / TLS 1.3, AES, CHACHA20 и др.), даже при наличии самоподписанного или тестового корпоративного сертификата, система распознаёт его как **Стандартный TLS** и запускает встроенный **Chromium Playwright**.
- Если сервер требует российские криптографические алгоритмы ГОСТ (ГОСТ Р 34.12-2015 «Кузнечик»/«Магма», ГОСТ Р 34.10-2012, 28147-89), которые не поддерживаются штатным OpenSSL в Python, система идентифицирует **ГОСТ TLS**.
2. **Автопоиск браузера для ГОСТ**:
- Система автоматически сканирует стандартные каталоги установки:
- `%LOCALAPPDATA%\Chromium-Gost\Application\chrome.exe`
- `%PROGRAMFILES%\Chromium-Gost\chrome.exe` (и в `Program Files (x86)`)
- **Яндекс.Браузер** (при установленном КриптоПро CSP): `%LOCALAPPDATA%\Yandex\...`, `%PROGRAMFILES%\Yandex\...`
- Системный `PATH` (`chromium-gost`, `yandex-browser`).
- Если браузер найден в системе, тесты стартуют автоматически без необходимости вручную прописывать путь в `.env`.
3. **Варианты запуска под ГОСТ**:
- **Автозапуск (рекомендуется)**: Установить [Chromium-GOST](https://github.com/deemru/chromium-gost/releases) или Яндекс.Браузер с КриптоПро CSP. При нестандартном расположении указать в `.env`:
```ini
CHROMIUM_GOST_PATH=C:\CustomPath\chrome.exe
```
- **Подключение по CDP (если браузер уже запущен вручную)**:
```powershell
chrome.exe --remote-debugging-port=9222 --no-first-run
```
Тесты автоматически подключатся к открытому браузеру на порту `9222`.
4. **Управление режимом TLS**:
- Через аргумент CLI:
```powershell
.\venv\Scripts\pytest.exe --tls-mode=auto # Автоопределение (по умолчанию)
.\venv\Scripts\pytest.exe --tls-mode=gost # Принудительно ГОСТ
.\venv\Scripts\pytest.exe --tls-mode=standard # Принудительно Стандартный
```
- Либо через `.env`:
```ini
TLS_MODE=auto # auto | gost | standard
```
5. **Отображение в HTML-отчёте**:
- В блоке **Environment** сгенерированного отчёта `html-report/report.html` фиксируются:
- Реальный режим TLS и использованный шифр (например, `Стандартный TLS (TLSv1.3, TLS_AES_256_GCM_SHA384)`).
- Тип и путь фактически запущенного браузера (`Chromium (Playwright)` / `Chromium GOST` / `Яндекс.Браузер`).
---
## Создание нового теста
### Нужен новый бизнес-процесс?
Опиши его в свободной форме — например:
> *«Нужен тест для ветстанции: открыть список животных, применить фильтр
> по кошкам, убедиться что список отфильтровался, открыть карточку
> первого животного, проверить наличие кнопки редактирования»*
ИИ-ассистент создаст файл в нужном месте, следуя архитектуре проекта.
---
### Если хочешь создать вручную — шаблон:
#### 1. Создать папку бизнес-процесса
```powershell
# Для роли vetstation, новый бизнес-процесс BP02
mkdir tests\02_vetstation\BP02_animals
New-Item tests\02_vetstation\BP02_animals\__init__.py
```
#### 2. Создать файл теста
Имя файла: `test_bp02_<название>.py`
```python
import os
import pytest
from playwright.sync_api import Page, expect
BASE_URL = os.getenv("BASE_URL", "https://vet.goznak.ru")
@pytest.mark.vetstation # <- маркер роли
@pytest.mark.usefixtures("browser_page") # <- авторизация и браузер
class TestBP02Animals:
"""
BP02: Краткое описание бизнес-процесса
Проверяет что пользователь может:
- Шаг 1: ...
- Шаг 2: ...
"""
@pytest.mark.dependency(name="BP02::s01")
def test_s01_<название>(self, browser_page: Page, bp_state: dict):
"""Шаг 1: Описание шага — именно это появится в отчёте"""
# ... код теста ...
bp_state["key"] = "value" # передать данные следующему шагу
@pytest.mark.dependency(name="BP02::s02", depends=["BP02::s01"])
def test_s02_<название>(self, browser_page: Page, bp_state: dict):
"""Шаг 2: Описание шага"""
value = bp_state["key"] # получить данные от предыдущего шага
# ... код теста ...
```
#### 3. Правила именования
| Элемент | Формат | Пример |
|---|---|---|
| Папка БП | `BP<NN>_<описание>` | `BP02_animals` |
| Файл теста | `test_bp<NN>_<описание>.py` | `test_bp02_animals.py` |
| Класс | `TestBP<NN><Описание>` | `TestBP02Animals` |
| Метод (шаг) | `test_s<NN>_<описание>` | `test_s01_open_list` |
| Зависимость | `BP<NN>::s<NN>` | `BP02::s01` |
#### 4. Маркеры ролей
| Маркер | Роль |
|---|---|
| `@pytest.mark.vetdept` | Управление ветеринарии |
| `@pytest.mark.vetstation` | Ветеринарная станция |
| `@pytest.mark.prefecture` | Администрация (префектура) |
---
## Добавление генератора данных (ФЛК)
Файл: [`tests/testdata/generators.py`](tests/testdata/generators.py)
Содержит пошаговое руководство по добавлению нового генератора прямо в начале файла. Коротко:
```python
# Шаблон новой функции-генератора:
def gen_<сущность>_<поле>() -> str:
"""
Краткое описание.
ФЛК:
- Правило 1
- Правило 2
Пример вывода: 'AB1234'
"""
# ... реализация ...
```
Использование в тесте:
```python
from tests.testdata.generators import gen_microchip, gen_animal_name
def test_s01_fill_form(self, browser_page, bp_state):
name = gen_animal_name()
chip = gen_microchip()
browser_page.fill("#name", name)
browser_page.fill("#chip", chip)
bp_state["name"] = name # передаём следующему шагу
```
---
## Добавление статического артефакта
1. Положи файл в папку `fixtures/` нужной роли:
```
tests/01_vetdept/fixtures/images/cat_photo.jpg
```
2. Раскомментируй шаблон фикстуры в `tests/<роль>/conftest.py`:
```python
@pytest.fixture(scope="session")
def cat_photo(fixtures_dir) -> Path:
"""Фото кошки для теста загрузки."""
return fixtures_dir / "images" / "cat_photo.jpg"
```
3. Используй в тесте:
```python
def test_s02_upload(self, browser_page, bp_state, cat_photo):
browser_page.set_input_files("input[type=file]", str(cat_photo))
```
---
## Создание теста через визуальный рекордер (Codegen)
Playwright Codegen — встроенный инструмент записи действий в браузере.
Ты кликаешь, вводишь текст и переходишь по страницам — Codegen генерирует
Python-код, который воспроизводит твои действия.
### Запуск рекордера
#### Вариант 1: Универсальный запуск с поддержкой ГОСТ TLS (рекомендуемый)
Штатная команда `playwright codegen` не умеет работать со сторонними браузерами напрямую. Для записи тестов на сайтах с **ГОСТ TLS** (а также обычным TLS) используйте встроенный скрипт `codegen.py`:
```powershell
# Запуск для URL по умолчанию из .env (с автоопределением TLS и выбором браузера):
.\venv\Scripts\python.exe codegen.py
# Принудительный запуск в режиме ГОСТ TLS (через Chromium-GOST):
.\venv\Scripts\python.exe codegen.py --gost
# Запуск для произвольного адреса:
.\venv\Scripts\python.exe codegen.py https://vet.goznak.ru
```
#### Вариант 2: Штатный запуск (только для сайтов со стандартным TLS)
```powershell
# Открыть встроенный Chromium с рекордером
.\venv\Scripts\playwright.exe codegen https://vet.goznak.ru
# Открыть рекордер и сразу сохранить результат в файл
.\venv\Scripts\playwright.exe codegen https://vet.goznak.ru --output tests\codegen_draft.py
```
При запуске откроются два окна:
- **Браузер (Chromium-GOST или Playwright Chromium)** — кликайте, заполняйте формы и переходите по разделам.
- **Playwright Inspector** — окно записи, в котором в реальном времени формируется Python-код.
### Порядок работы
1. **Запусти рекордер** командой выше
2. **Авторизуйся** под нужной ролью (введи логин/пароль из `.env`)
3. **Выполни нужный бизнес-процесс** — все действия записываются автоматически
4. **Скопируй сгенерированный код** из Playwright Inspector
5. **Адаптируй код** под архитектуру проекта (см. ниже)
---
### Как адаптировать код из Codegen в архитектуру проекта
Codegen генерирует **плоский скрипт**, проект использует **класс с шагами**.
Ниже показано как преобразовать одно в другое.
#### До (вывод Codegen):
```python
from playwright.sync_api import Playwright, sync_playwright, expect
def run(playwright: Playwright) -> None:
browser = playwright.chromium.launch(headless=False)
context = browser.new_context()
page = context.new_page()
page.goto("https://vet.goznak.ru/auth")
page.get_by_role("textbox", name="Email").fill("gz_vetstation@vet.goznak.ru")
page.get_by_role("textbox", name="Введите пароль").fill("A6wrpzpD5T")
page.get_by_role("button", name="Войти в аккаунт").click()
page.get_by_role("link", name="Животные").click()
expect(page).to_have_url("https://vet.goznak.ru/animals")
page.get_by_role("button", name="Добавить животное").click()
page.get_by_label("Кличка").fill("Мурка")
page.get_by_role("button", name="Сохранить").click()
expect(page.get_by_text("Мурка")).to_be_visible()
context.close()
browser.close()
with sync_playwright() as playwright:
run(playwright)
```
#### После (архитектура проекта):
```python
import os
import pytest
from playwright.sync_api import Page, expect
BASE_URL = os.getenv("BASE_URL", "https://vet.goznak.ru")
@pytest.mark.vetstation
@pytest.mark.usefixtures("browser_page")
class TestBP02AddAnimal:
"""
BP02: Добавление нового животного
Проверяет что пользователь может:
- Перейти в раздел Животные
- Открыть форму добавления
- Заполнить кличку и сохранить
- Убедиться что животное появилось в списке
"""
# Авторизация и логин/пароль — убраны полностью:
# этим занимается фикстура browser_page (см. tests/conftest.py)
@pytest.mark.dependency(name="BP02::s01")
def test_s01_go_to_animals(self, browser_page: Page, bp_state: dict):
"""Шаг 1: Перейти в раздел Животные"""
browser_page.get_by_role("link", name="Животные").click()
expect(browser_page).to_have_url(BASE_URL + "/animals", timeout=10000)
@pytest.mark.dependency(name="BP02::s02", depends=["BP02::s01"])
def test_s02_open_add_form(self, browser_page: Page, bp_state: dict):
"""Шаг 2: Открыть форму добавления животного"""
browser_page.get_by_role("button", name="Добавить животное").click()
expect(browser_page.get_by_label("Кличка")).to_be_visible(timeout=10000)
@pytest.mark.dependency(name="BP02::s03", depends=["BP02::s02"])
def test_s03_fill_and_save(self, browser_page: Page, bp_state: dict):
"""Шаг 3: Заполнить кличку и сохранить"""
from tests.testdata.generators import gen_animal_name
name = gen_animal_name() # случайное имя по ФЛК
browser_page.get_by_label("Кличка").fill(name)
browser_page.get_by_role("button", name="Сохранить").click()
bp_state["animal_name"] = name # передаём шагу 4
@pytest.mark.dependency(name="BP02::s04", depends=["BP02::s03"])
def test_s04_verify_in_list(self, browser_page: Page, bp_state: dict):
"""Шаг 4: Проверить что животное появилось в списке"""
expect(
browser_page.get_by_text(bp_state["animal_name"])
).to_be_visible(timeout=10000)
```
### Что изменилось при адаптации
| Было в Codegen | Стало в проекте |
|---|---|
| `browser.launch()` + `context.new_page()` | Фикстура `browser_page` (авто) |
| Явный логин/пароль в коде | Берётся из `.env` через фикстуру |
| Один плоский `def run()` | Класс с методами-шагами |
| Нет зависимостей между шагами | `@pytest.mark.dependency(depends=[...])` |
| Хардкод данных (`"Мурка"`) | Генераторы ФЛК (`gen_animal_name()`) |
| Нет передачи данных между шагами | `bp_state["key"] = value` |
| Нет отображения в отчёте | Docstring шага → колонка Test в отчёте |