Скрипты и расширение

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

Что такое скрипт

Скрипт — это файл с кодом на Python. Он лежит в папке проекта рядом с правилами и действиями и хранится вместе с проектом в git, то есть попадает в ту же историю версий и проходит тот же контроль, что и остальные файлы проекта.

Перед каждым запуском продукт читает файл заново. Поэтому правка вступает в силу сразу, без перезапуска сервера: сохранили файл — следующий запуск выполнит уже новый код. Зачем это нужно: продукт перестаёт быть набором готовых заготовок. Новый вид проверки заводится там, где живёт проект, — например, проверка адреса API, счётчика в базе данных или нужного файла на диске.

Где живёт скрипт

Скрипты живут в папке scripts/ внутри проекта. В ней три вещи:

  • scripts.yml — опись: название скрипта, зачем он нужен, включён он или выключен, свой таймаут, разрешённые переменные сервера и свои переменные.
  • <id>.py — сам код. Имя файла и есть имя скрипта в описи.
  • requirements.txt — необязательный список библиотек, которые скрипту нужны.

Всё, что скрипту нужно для работы, но не является его кодом — окружение Python и рабочие каталоги для промежуточных файлов, — продукт держит в своих данных (папка TEST_APP_DATA, в образе — /apps/test), а не в папке проекта.

Куда можно подключить скрипт

Один и тот же файл подключается к пяти точкам работы конвейера. Чем полезна каждая:

  • Шаг теста. Проверка адреса API, базы данных или файла прямо в прогоне — рядом с шагами, которые работают в браузере. Подробности — в разделе Тесты.
  • Тест целиком из скриптов. Если все шаги теста — скрипты, браузер не нужен: тест выполняется на сервере, а очередь, расписание и внешний доступ работают как обычно. В строке исполнителя такого прогона будет указан сервер — это видно в разделе Запуски.
  • Движок сравнения снимков. Скрипт сам сравнивает два снимка и отвечает, на сколько процентов они отличаются: свой алгоритм вместо встроенного.
  • Классификатор отличий. Скрипт смотрит на спорные отличия до того, как за дело возьмётся модель, и решает, важное это отличие или случайный шум. Он может только повысить важность, но не понизить её и ничего не одобряет.
  • Обработчик после прогона. Когда прогон завершился, продукт передаёт внешнему коду его сводку — так делаются уведомления, отчёты и выгрузки.

Кроме этого, любой скрипт можно запустить вручную — с любыми аргументами, чтобы проверить его до подключения. Движок сравнения и классификатор описаны в разделе Визуальное тестирование.

Вкладка «Скрипты»

Работать со скриптами удобнее всего из отдельной вкладки «Скрипты» в окне проекта. В ней видно:

  • Список скриптов со значками «используется в» — сразу понятно, какие тесты и настройки на скрипт ссылаются.
  • Редактор кода с подсветкой Python и полем заметки «зачем нужен этот скрипт». Заметка помогает вспомнить назначение через полгода.
  • Настройки скрипта: свой таймаут, разрешённые переменные сервера и свои переменные.
  • Пробный запуск с аргументами в виде JSON — так скрипт проверяют до того, как подключать его к тестам.
  • Журнал запусков — все запуски этого скрипта на одном экране.

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

Что получает скрипт и что возвращает

Договор простой. На вход скрипт получает один объект JSON: какая точка его вызвала, что за прогон идёт, какие аргументы подключения заданы и сведения самой точки — например, имена файлов со снимками. На выход идёт последний объект JSON, который скрипт напечатал; строки, напечатанные раньше, не мешают.

То же самое скрипт видит и переменными окружения: TEST_APP_SCRIPT_KEY — имя самого скрипта, TEST_APP_POINT — точка подключения, TEST_APP_RUN_ID — номер записи в журнале (по нему запуск легко найти потом), TEST_APP_WORK_DIR — рабочий каталог запуска. Переменных сервера среди них нет — только разрешённые вами и свои.

Что именно вернуть — зависит от точки. Шаг теста отвечает «получилось или нет» и может вернуть строки проверок; движок сравнения — процент отличий; классификатор — класс отличия; обработчик прогона — что угодно. Вот короткий скрипт, который проверяет адрес API:

import json, sys, urllib.request

data = json.loads(sys.stdin.read())
url = data.get("args", {}).get("url")

with urllib.request.urlopen(url, timeout=20) as resp:
    ok = resp.status == 200

print(json.dumps({"ok": ok, "output": "адрес отвечает" if ok else "адрес не отвечает"}))

Все остальные подробности договора — как устроен объект на входе и что ждёт каждая точка на выходе — собраны в разделе Внешний доступ для агентов.

Переменные и секреты

Скрипт видит не всё окружение сервера, а только то, что ему разрешили: список разрешённых переменных сервера и свои переменные. Значение вида $ИМЯ подставляется из окружения во время запуска и нигде не хранится. Значения с «секретными» именами — например, token, secret, password — читаются как ***, так что случайно перезаписать пароль местами нельзя.

Зачем так сделано: в файлах проекта не должно оказаться паролей — они живут в переменных, как и у действий. Как заводить сами переменные и учётные данные, описано в разделе Среды, учётные данные и переменные.

Что видно про каждый запуск

Каждый запуск оставляет ровно одну запись в журнале — и удачный, и неудачный. В записи видно точку подключения, источник (вручную, расписание, внешний доступ, прогон), статус, длительность и хвост ошибки, если она была. Неудачи названы словами, а не кодами: выполнение отключено, нет Python, превышено время, код завершился с ошибкой, файл скрипта пропал, непонятный вывод, слишком много вывода, запуск отменён.

Записи хранятся 30 дней по умолчанию. Журнал — первое место, куда стоит смотреть, если скрипт ведёт себя не так, как ожидалось: по нему видно, дошло ли дело до запуска и что именно ответил скрипт.

Среда исполнения и библиотеки

Своё окружение Python заводится для каждого проекта отдельно и создаётся при первом запуске. Интерпретатор берётся из переменной TEST_APP_PYTHON (по умолчанию python3); от него и строится окружение. Библиотеки из requirements.txt ставятся один раз и обновляются только тогда, когда файл изменился, — лишней работы при каждом запуске не происходит.

Если Python не найден или установка библиотек не удалась, продукт говорит об этом прямо в предупреждении на вкладке и записывает причину в журнал. Тихого пропуска, когда скрипт вроде бы есть, но никогда не работает, не бывает.

Предохранители

У скриптов есть пределы — их можно менять в настройках проекта, но совсем снять нельзя:

  • Время одного запуска. По умолчанию 60 секунд, потолок — 600 секунд. По истечении времени уничтожается всё дерево процессов, включая дочерние, так что зависший скрипт не остаётся работать втихую.
  • Число одновременных запусков. По умолчанию два; остальные ждут своей очереди.
  • Объём вывода. Ограничен примерно мегабайтом: результат нужно печатать, а объёмные данные — складывать в файлы рабочего каталога.
  • Число скриптов и размер кода. До 200 скриптов на проект и до 256 КБ кода на каждый.
  • Срок хранения журнала. 30 дней по умолчанию.

Есть и два выключателя: общий выключатель сервера TEST_APP_SCRIPTS_ENABLED и выключатель проекта в его настройках. Выключенный скрипт не становится «зелёным» молча: попытка запуска попадает в журнал с причиной — «выполнение отключено».

Честные границы

  • Изолированной среды нет. Скрипт работает от имени сервера и имеет его доступ к файлам и сети. Это код доверенного человека, который хранится вместе с проектом, поэтому запускать чужие скрипты нельзя. Пределы выше — защита от случайной ошибки, а не изоляция.
  • Только Python. Других языков продукт не поддерживает — это осознанное ограничение, а не недоделка.
  • Постоянно работающих служб нет. Каждый запуск — отдельный процесс, который завершается вместе с работой.
  • Скрипт ничего не одобряет. Даже классификатор может только повысить важность отличия. Одобрение эталона и вердикт по отличиям остаются за человеком.

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

← Оглавление документации