# 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 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_<описание>` | `BP02_animals` | | Файл теста | `test_bp_<описание>.py` | `test_bp02_animals.py` | | Класс | `TestBP<Описание>` | `TestBP02Animals` | | Метод (шаг) | `test_s_<описание>` | `test_s01_open_list` | | Зависимость | `BP::s` | `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 в отчёте |