# Региональное QA: проверяем план до браузера

Региональная проверка может дать другой текст страницы после смены прокси. Но одновременно могли измениться язык браузера, cookies, часовой пояс или разрешение геолокации. Чтобы не приписать всё сетевому адресу, сначала проверьте сам план опыта. Ниже — CSV и маленький локальный валидатор: он находит случаи, где вместо одного фактора случайно поменяли два.

**Раскрытие связи:** материал подготовлен Portproof с помощью ИИ и последующей редакционной проверкой. Компания продаёт прокси и другие продукты для подключения. Все случаи и обозначения ниже вымышленные; браузер, аккаунт, географию выхода и доступность внешнего сайта здесь не испытывали. Это проверка файла плана, а не выполнение регионального теста.

## Разделите пять независимых настроек

В [документации Playwright по эмуляции](https://playwright.dev/docs/emulation) язык, часовой пояс, геолокация и разрешения задаются явно. Настройка прокси отвечает за другой слой — маршрут соединения. Наличие такого параметра в инструменте не доказывает, что конкретный сайт будет использовать его для выбора содержимого.

Cookies относятся к состоянию браузера и аккаунта. Sticky-сессия прокси не сохраняет их и не создаёт авторизованный вход. И наоборот, новый контекст браузера сам по себе не выбирает другую сеть. Результат страницы нужно записать отдельно от этих входных условий.

Для собственного продукта сначала определите ожидаемое поведение и разрешённую область проверки. Сторонний публичный ресурс может иметь другие условия доступа; отказ или ограничение требуют остановки и разбора, а не маскировки клиента. [План регионального QA](https://portproof.org/ru/use-cases/qa-geo-testing) помогает отделить задачу от обещания результата.

## Назовите базовый случай

Базовая строка фиксирует отправную точку. Каждая следующая отличается от неё ровно одним из пяти факторов. Значения `proxy_a`, `saved_a` и `fictional_point` — условные подписи, не рабочие адреса, cookies или координаты.

```csv
case,proxy,cookies,locale,timezone,geolocation,changed,permission
baseline,direct,clean,ru-RU,UTC,unset,baseline,owned
route_only,proxy_a,clean,ru-RU,UTC,unset,proxy,owned
locale_only,direct,clean,en-GB,UTC,unset,locale,owned
cookies_only,direct,saved_a,ru-RU,UTC,unset,cookies,owned
time_only,direct,clean,ru-RU,Europe/Paris,unset,timezone,owned
location_only,direct,clean,ru-RU,UTC,fictional_point,geolocation,owned
```

`permission` — записанная человеком отметка. Слово `owned` не выдаёт разрешение и не доказывает право на данные. Перед реальным запуском ответственный должен проверить полномочия и правила ресурса вне этого файла. Если подтверждения нет, пометьте случай как требующий рассмотрения и не запускайте его.

Шесть строк не описывают все комбинации мира. Это стартовая диагностическая серия. Если продукт специально зависит от комбинации факторов, её нужно добавить отдельным этапом с собственной гипотезой. Приведённый валидатор намеренно отклонит такую строку в серии «один фактор».

## Найдите испорченный случай до запуска

Сохраните код как `matrix.py`. Он читает встроенный пример через стандартный [csv.DictReader](https://docs.python.org/3/library/csv.html), проверяет столбцы, имена и отличие от базовой строки. Сеть и браузер не запускаются; адрес назначения даже не входит в структуру файла.

```python
import csv
import io
import json

CSV = """case,proxy,cookies,locale,timezone,geolocation,changed,permission
baseline,direct,clean,ru-RU,UTC,unset,baseline,owned
route_only,proxy_a,clean,ru-RU,UTC,unset,proxy,owned
locale_only,direct,clean,en-GB,UTC,unset,locale,owned
cookies_only,direct,saved_a,ru-RU,UTC,unset,cookies,owned
time_only,direct,clean,ru-RU,Europe/Paris,unset,timezone,owned
location_only,direct,clean,ru-RU,UTC,fictional_point,geolocation,owned
"""


def validate(text):
    factors = ["proxy", "cookies", "locale", "timezone", "geolocation"]
    fields = ["case", *factors, "changed", "permission"]
    reader = csv.DictReader(io.StringIO(text))
    if reader.fieldnames != fields:
        raise ValueError("Unexpected columns")
    rows = list(reader)
    if not rows or len(rows) > 100:
        raise ValueError("Expected 1 to 100 cases")
    if any(None in row or any(not value for value in row.values()) for row in rows):
        raise ValueError("Missing cell or extra column")
    names = [row["case"] for row in rows]
    if len(set(names)) != len(names) or names.count("baseline") != 1:
        raise ValueError("Require one baseline and unique case names")
    baseline = next(row for row in rows if row["case"] == "baseline")
    if baseline["changed"] != "baseline":
        raise ValueError("Baseline must be labelled")
    for row in rows:
        if row["permission"] not in {"owned", "authorised"}:
            raise ValueError("Permission marker needs human review")
        actual = [key for key in factors if row[key] != baseline[key]]
        if row is baseline:
            continue
        if actual != [row["changed"]]:
            raise ValueError("Change exactly the named factor: " + row["case"])
    return {
        "fictional": True,
        "cases": len(rows),
        "single_factor_variants": len(rows) - 1,
        "network_or_browser_executed": False,
        "permission_independently_verified": False,
    }


if __name__ == "__main__":
    print(json.dumps(validate(CSV), indent=2))
```

Для исходного примера результат показывает 6 случаев и 5 вариантов с одним фактором. Поля `network_or_browser_executed: false` и `permission_independently_verified: false` сохраняют границу результата. Чтобы увидеть защиту, поменяйте язык ещё и в строке `route_only`: проверка должна остановиться, а не отметить такой файл готовым.

Это не автоматическая проверка действительности cookies, часового пояса или сетевого адреса. Валидатор сравнивает строки. Неверное, но одинаково написанное значение может пройти. Он также не определяет, какие разрешения доступны на устройстве и что реально увидел сервер. Следующий этап требует самого инструмента, разрешённого окружения и наблюдений.

## Что записать во время настоящего QA

Заранее добавьте к рабочему отчёту время, версию клиента, идентификатор случая, ожидаемый признак, статус ответа и фактический текст или безопасный снимок. Отмечайте неизвестное отдельно. Успешный CONNECT, HTTP200, подходящий язык и правильная цена — разные проверки; одна не подтверждает остальные.

Для поиска используйте [отдельный порядок сравнения выдачи по странам](https://portproof.org/ru/guides/check-search-results-in-another-country). Сам факт выбора страны прокси не устанавливает локальный спрос, позицию сайта или язык результата. Удерживайте одинаковыми запрос, состояние браузера и время настолько, насколько позволяет реальная задача, а ограничения сравнения оставляйте в отчёте.

Если используете [Playwright с прокси](https://portproof.org/ru/guides/playwright-proxy), перенесите проверенные параметры в фактический контекст и убедитесь, что тест обращается именно из него. Не переносите пароль в клиентский код, URL назначения или общий отчёт. Не публикуйте cookies и содержимое чужого аккаунта ради воспроизводимости.

## Разделите проверку и сбор данных

Маленькая диагностическая серия не даёт разрешения на массовый обход страниц. Для [разрешённого сбора данных](https://portproof.org/ru/guides/proxies-for-web-scraping) отдельно определяют источники, допустимую частоту, объём, остановку и обработку отказов. Новая страна или сессия не превращают запрещённую операцию в допустимую и не гарантируют новый IP.

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

Утилита доступна без покупки и аккаунта. Ссылки Portproof ведут к связанным русским инструкциям; кабинет и оформление заказа остаются на английском. Локальная редакционная проверка подтверждает только поведение валидатора на учебных входах, без живых подключений, замеров сети и пользовательских операций.

Авторская локальная проверка выполнена 31 октября 2026 года на CPython 3.9.6: для этой утилиты проверены один корректный и пять ошибочных входов, без сетевых операций. Это граница проверки примера, а не рекомендация выбирать данную версию Python для нового проекта.

