Skip to content

Прогоны ​

Эндпоинты прогонов: импорт отчётов из CI, ручные прогоны и их результаты, корзина, конфигурации, статусы результатов, исследовательские сессии, майлстоуны, экспорт отчётов и публичные ссылки на них.

Список и чтение прогонов ​

  • GET /api/runs/search — одна страница прогонов, сначала новые, со счётчиками, но без результатов.
    • ?mine=1 — только прогоны, где у вызывающего осталась работа: выданные ему результаты, которые ещё не выполнены, или выданный ему прогон, который ещё идёт. У каждого есть mineLeft — сколько его результатов не проверено.
    • ?q=TC-100 — прогоны, в которых есть этот кейс, вдобавок к прогонам с таким названием.
    • ?milestone=MS-3 — см. Майлстоуны.
  • GET /api/runs/summary — итоги по всем прогонам и окружения, которые в них указаны.
  • GET /api/runs — самые новые прогоны вместе с результатами, оставлен для API-клиентов: 500, если ?limit= не задаёт иначе, не больше 5000, и не больше прогонов, чем вмещают 50 000 результатов в сумме (KVALLI_MAX_LISTED_RESULTS). Если более старые прогоны отброшены из-за этого, приходит заголовок X-Kvalli-Truncated: true.
  • GET /api/runs/:id — один прогон с результатами, reruns (прогоны, запущенные как его перезапуски) и первым миллионом символов загруженного отчёта в logs (logsTruncated: true, если отчёт длиннее).
  • GET /api/runs/:id/report — весь отчёт в том виде, в каком его отправили, как скачивание.
  • DELETE /api/runs/:id — удалить прогон (он попадает в корзину).

Изменения прогона возвращают прогон, но без logs.

  • PATCH /api/runs/:id/items/:itemId/case { caseId } — привязать результат к кейсу (runs:write и cases:write). Кейс запоминает ключи, под которыми известен автотест, так что тот же тест в следующем отчёте привяжется сам. Ответ — { run, learned }.
  • GET /api/runs/defect-tracker — { "ready": true }, если трекер задач подключён и по результату можно завести дефект (runs:read).

Корзина ​

  • GET /api/trash — удалённые кейсы и прогоны проекта, сначала новые, с указанием, кто и когда их удалил.
  • POST /api/trash/cases/:id/restore, POST /api/trash/runs/:id/restore — восстановить один.
  • DELETE /api/trash/cases/:id, DELETE /api/trash/runs/:id — удалить навсегда, вместе с файлами.
  • POST /api/trash/{kind}/restore, POST /api/trash/{kind}/purge { ids } для cases, runs, plans, milestones, sessions и requirements — до 500 за раз, с теми же правами, что нужны для удаления (удаление навсегда требует ещё cases:delete). Ответ — { done: [...ids], failed: [{ id, error }] }.

Тест-планы, майлстоуны, сессии и требования указываются по ключам в том виде, в каком они показаны (TP-3, MS-2, ES-4, ключ требования); GET /api/trash перечисляет их как { plans: { items, total }, … }. Восстановленный майлстоун снова собирает под собой прогоны, тест-планы, сессии и вложенные майлстоуны, которые всё ещё ни к чему не отнесены; сессия возвращается со своими заметками, требование — со связями с кейсами. Ежедневная очистка удаляет то, что пролежало в корзине 30 дней. DELETE /api/testcases/:id и DELETE /api/runs/:id перемещают записи в корзину, а не удаляют их.

Импорт отчёта из CI ​

POST /api/runs/import — импорт отчёта из CI. Максимальный размер — 50 МБ. Нужно право runs:import: оно есть у каждой роли, которая записывает прогоны, и у ключа с runs:write.

Архивы не распаковываются

На сервере нет обработки архивов: здесь ничего не распаковывается. ZIP с результатами Allure распаковывает в браузере диалог импорта, который загружает объединённый JSON через POST /api/runs; пайплайн не может отправить архив на этот маршрут.

Что принимает маршрут и как он определяет формат:

ТелоКак читается
{ name?, fileType?, rawContent, source?, author?, milestone? }fileType как указан, junit, если не указан
тело не JSON (text/*, application/xml, application/octet-stream, отправка формы), начинающееся с <JUnit XML
тело не JSON, не начинающееся с <Playwright JSON
JSON-объект без rawContentPlaywright JSON

Таким образом, тело без обёртки — это всегда JUnit или Playwright. Чтобы импортировать что-то другое — Allure CSV или результаты Allure, уже распакованные в JSON, — оберните тело и назовите формат: { fileType: "allure-csv", rawContent: "…" }. На этом маршруте fileType не сверяется со списком; проверяет его POST /api/runs (junit, playwright, csv, allure-csv, allure, allure-zip), и allure-zip там тоже означает уже распакованный JSON, а не байты архива. Пустое тело получает 400.

  • ?environment= (или ?env=) и ?release= (или ?version=) записывают, где выполнялся прогон и на какой версии. Необязательный свободный текст до 64 символов; обёрнутое тело может передать их как поля. Один и тот же набор, падающий на staging и проходящий на review-стенде, — это два факта, а не один нестабильный тест.
  • ?milestone= относит прогон к майлстоуну, названному ключом (MS-3) или точным названием (без учёта регистра). Несуществующий или завершённый майлстоун не ломает загрузку: прогон ни к чему не относится, а ответ объясняет причину в warning.
  • buildId, branch, commit, buildUrl (в запросе или в обёрнутом теле) записывают сборку CI. С buildId повторная загрузка той же сборки отвечает 200 с уже созданным прогоном (duplicate: true), а не создаёт второй.
  • Наборы JUnit могут быть вложенными; падение сохраняет сообщение и текст как стек. Результат теста Playwright — его итоговый результат (прошёл при повторе — значит прошёл, с пометкой «нестабилен»), и каждый проект Playwright — отдельный результат.
  • x-api-key, который недействителен, истёк или отозван, получает 401; он никогда не принимается за другого вызывающего. Так отправляют отчёты клиенты из раздела Отправка результатов из CI.

POST /api/runs { name, fileType, rawContent, environment?, release?, milestoneKey?, source? } — загрузка, которую отправляет диалог импорта (только JSON).

Привязка результатов к кейсам ​

  • Ключи кейсов в названиях тестов (TC-101 checkout succeeds, @case: TC-101) привязывают каждый результат к его тест-кейсу; то же делают JUnit-свойство <property name="case" value="TC-101"/> у теста и, в отчётах Playwright, аннотации и теги.
  • Результат, не называющий кейс, привязывается по автотесту. automationRef кейса — полное имя теста в том виде, в каком его пишет Allure (module#test для pytest, pkg.Class.method для JUnit и TestNG, file.spec.ts › Describe › title или file.spec.ts:21:3 для Playwright, vitest и jest). Оно сопоставляется с classname и name в JUnit, с файлом, блоками describe, названием и позицией в Playwright или с fullName результата Allure.
  • Метка AS_ID результата Allure или allure_id в Allure CSV привязывает его к кейсу, перенесённому из этого тест-кейса TestOps.
  • Совпадение, общее для нескольких кейсов, ничего не привязывает; ключ кейса в тесте всегда важнее. Кейсы, импортированные из Allure TestOps, несут свой automationRef, поэтому их результаты привязываются без изменений в тестах.
  • POST|PUT /api/testcases принимают automationRef — автотест, которому соответствует кейс; очистка значением "" отвязывает будущие результаты.
  • Результат Playwright сохраняет шаги test.step (вложенные — под именем родителя: Log in › Submit the form), и из JSON-репортера, и из репортера Kvalli; шаг, который упал, — failed.

Кейсы из результатов прогона ​

POST /api/runs/:id/cases-from-results { itemIds?, intoFolder? } (runs:write и cases:write) — для каждого теста CI-прогона, чьи результаты не привязаны ни к одному кейсу, создаёт черновик кейса. В интерфейсе это кнопка «Создать кейсы (N)» в прогоне.

  • Название — название теста. Имя, записанное как код, переводится в слова: addsItem — «Adds item», test_pays_by_card — «Pays by card».
  • Папки лежат внутри intoFolder (интерфейс передаёт имя прогона без номера сборки):
    • Playwright — spec-файл и блоки describe;
    • JUnit — класс без пакета: com.shop.CartTest$Checkout → Cart › Checkout;
    • pytest — модуль и класс: tests.test_cart.TestCart → cart › Cart;
    • vitest и jest — файл и блоки describe.
  • Шаги кейса — шаги test.step из результата; если тест шёл в нескольких браузерах, берётся тот, где шагов больше.
  • automationRef — полное имя теста, поэтому результаты этого прогона привязываются сразу, а следующего отчёта — сами.
  • Один кейс на тест, в скольких бы браузерах и с какими бы параметрами он ни шёл (test_x[a-1] и test_x[a-2] — один кейс). Тест, которому уже соответствует какой-то кейс, привязывается к нему, а не получает второй. Повторный вызов ничего не создаёт.
  • Учитываются только результаты с ключами автотеста, до 1000 за вызов; itemIds сужает выбор.
  • Ответ: { run, created: [{ key, title }], reused, linked }; 201, если кейс создан, иначе 200.

Прогон, который заполняется по ходу тестов ​

Так работает kvalli watch (см. CI) — как allurectl watch в Allure TestOps. Все три запроса требуют runs:import.

  • POST /api/runs/live { name?, fileType?, source?, environment?, release?, buildId?, branch?, commit?, buildUrl?, pipelineId? } — открыть пустой прогон в статусе running с live: true (201). Тот же buildId возвращает уже открытый прогон (200, duplicate: true). Прогон сразу связывается со своим пайплайном, поэтому запуск плана видит его в прогрессе.
  • POST /api/runs/:id/live { fileType, rawContent } — результаты, закончившиеся с прошлой порции. Результат Allure узнаётся по historyId: повтор теста из более поздней порции заменяет его прежнюю попытку. Ответ — счётчики прогона на сейчас и сохранённые результаты этой порции (tests, с их id для файлов). Статус остаётся running. Завершённый прогон отвечает 409.
  • POST /api/runs/:id/finish { durationMs? } — завершить: статус по результатам, длительность — сколько шла джоба. Повторный вызов ничего не меняет; воркфлоу получают событие импорта один раз.

Пока прогон идёт, его страница обновляется сама и показывает «Результаты поступают».

Вебхуки пайплайнов ​

GET /api/hooks, POST /api/hooks { sourceId }, DELETE /api/hooks/:id — вебхуки пайплайнов для репозиториев-источников проекта (settings:write, модуль ci).

  • POST создаёт вебхук источника или выдаёт ему новый секрет и отвечает { hook: { id, path, provider }, secret }; секрет больше никогда не показывается.
  • POST /api/hooks/gitlab/:id и POST /api/hooks/github/:id — вызовы со стороны провайдера: без сессии, проверяются секретом (GitLab — X-Gitlab-Token, GitHub — X-Hub-Signature-256), сразу отвечают 202. Завершённый пайплайн становится прогоном так же, как при импорте (см. Отправка результатов из CI).

Ручные прогоны ​

Запуск ​

POST /api/runs/manual — запустить прогон, который будет выполнять человек, по тест-плану (planKey) или по списку caseIds, с необязательными assignee, environment, release и milestoneKey (404 для несуществующего майлстоуна, 409 для завершённого).

  • configurations — варианты, выбранные в каждой группе, например { "Browser": ["Chrome", "Firefox"], "OS": ["Windows"] }, — запускает по прогону на каждую комбинацию, с названиями вида Name · Chrome · Windows, каждый с configuration: [{ group, option }]. В ответе run — первый прогон, runs — все. Названия сравниваются без учёта регистра; название, которого нет в проекте, или больше 25 прогонов — 400.
  • Каждый результат начинается как untested; если тест-план называет кейс, которого больше нет, он указан в missing.
  • Кейс с parameters даёт по результату на каждую строку, с названием вида TC-7 Title [browser=chrome]; значения подставляются в скопированные шаги и сохраняются на результате как parameters: [{ name, value }].

Результаты ​

  • PATCH /api/runs/:id/items/:itemId — записать вердикт (passed, failed, blocked, skipped, untested или ключ одного из собственных статусов результатов проекта) и необязательный comment. Статус проекта хранится как базовый вердикт, которому он соответствует, и указывается рядом в customStatus. Прогон пересчитывается по результатам: он остаётся running, пока что-то не проверено, а падение заглушённого кейса не роняет его.
  • PATCH /api/runs/:id/verdicts (runs:write) — записать вердикт сразу для нескольких результатов одного прогона одним запросом, чтобы отметить пачку в окне ручного прогона. К каждому применяются те же правила, что и для одиночного маршрута.
  • GET /api/runs/:id/items/:itemId/history (runs:read) — как этот тест проходил в более ранних прогонах проекта; это показывает панель результата рядом с падением, и так становится виден нестабильный тест.
  • PATCH /api/runs/:id/items/:itemId/steps/:index — записать результат одного шага (status: passed, failed, blocked, skipped, untested) и то, что на самом деле наблюдалось (actual; пустая строка очищает его). index считается с 0 по шагам, которые прогон скопировал из кейса при запуске. Вердикт кейса не меняется. 404 для шага за пределами списка.
  • PATCH /api/runs/:id/assignee — { itemIds, assignee } выдаёт результаты человеку или забирает их при assignee: null; id, не являющиеся результатами этого прогона, пропускаются, а assigned говорит, сколько изменено. После этого у каждого результата есть свой assignee.

Файлы результатов ​

  • POST /api/runs/:id/items/:itemId/attachments — сохранить файл у одного результата: скриншот падения, видео, трассу. Файл — это сырое тело запроса (curl --data-binary @shot.png -H "Content-Type: image/png"), имя задаётся в X-Filename (в percent-encoding для имён не в ASCII) или в ?filename=. До 25 МБ на файл и 50 файлов на результат; 404 для результата не из указанного прогона. Нужно runs:import, так что файлы может отправлять CI-ключ. GET /api/runs/:id перечисляет файлы каждого результата в attachments.
  • GET|DELETE /api/runs/:id/items/:itemId/attachments/:attachmentId — отдать или удалить файл. Отдаётся как скачивание; ?inline=1 показывает изображение или видео на месте. HTML, SVG и другие типы документов всегда отдаются как скачивание. Удаление прогона удаляет его файлы.

Дефекты по результатам ​

POST /api/runs/:id/items/:itemId/defect — завести дефект по упавшему или заблокированному результату в настроенном трекере и записать ключ задачи на этом результате. Любой другой вердикт получает 409, как и повторная попытка, когда ключ уже записан.

Перезапуск и закрытие ​

  • POST /api/runs/:id/rerun — новый ручной прогон по кейсам этого прогона в статусах failed и blocked (scope: "failed", по умолчанию) или по всем ("all", копия), с необязательными name и assignee. Окружение, релиз, тест-план и исполнитель каждого кейса переносятся, а новый прогон указывает своё происхождение в rerunOf. Параметризованный кейс перезапускается для тех строк, которые выполняли выбранные результаты, с теми значениями, что у них были, даже если его таблица с тех пор изменилась. На импортированном прогоне берутся результаты, привязанные к кейсу, по одному на кейс; непривязанные результаты считаются в notRerunnable. 400, если перезапускать нечего.
  • POST /api/runs/:id/close — сделать вердикты окончательными: до POST /api/runs/:id/reopen вердикты, результаты шагов, исполнители и файлы прогона получают 409. Если остались непроверенные кейсы, ответ — 409 с их числом в untested, если только skipUntested: true не помечает их пропущенными. Прогон записывает closedAt и closedBy.

Конфигурации ​

То, на чём выполняется прогон и что не указано в кейсах: браузер, операционная система, устройство.

  • GET /api/configurations — группы проекта, { id, name, options }, по порядку.
  • POST /api/configurations { name, options }, PUT /api/configurations/:id { name?, options? } и DELETE /api/configurations/:id требуют settings:write.

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

Статусы результатов ​

  • GET /api/result-statuses — собственные статусы проекта, { key, label, base }, например «Пройден с замечаниями», который считается как passed.
  • POST /api/result-statuses { label, base, key? }, PUT /:key{ label?, base? }, DELETE /:key требуют settings:write.

Ключ строится из названия; имена базовых вердиктов заняты. Изменение того, чем считается статус, или его удаление не переписывает результаты, которым он уже присвоен. В интерфейсе — «Настройки → Статусы результатов».

Исследовательские сессии ​

Чартер, который исследуют в рамках таймбокса (ES-1, ES-2, … в каждом проекте).

  • GET /api/sessions (сначала идущие, с noteCounts), GET /:key (с notes).
  • POST /api/sessions{ charter, area?, tester?, environment?, timeboxMinutes? (60), milestoneKey? }, PUT /:key, POST /:key/complete { summary? }, POST /:key/reopen, DELETE /:key.
  • POST /:key/notes { kind: note | bug | question | idea, body } — только пока сессия идёт (409 после завершения); PATCH и DELETE /:key/notes/:noteId.
  • POST /:key/notes/:noteId/defect { title?, dryRun? } заводит заметку как дефект в настроенном трекере и записывает на ней defectKey (dryRun: true отвечает 200 с названием и описанием, которые были бы отправлены, и ничего не записывает).
  • POST /:key/notes/:noteId/case делает из заметки черновик кейса (с названием по её первой строке, в папке Exploratory / область сессии) и записывает caseKey.

Каждое из двух последних действий выполняется один раз: повторный вызов получает 409.

Майлстоуны ​

Релиз, спринт или тестовый цикл, к которому относятся прогоны. Ключи — MS-1, MS-2, … в каждом проекте. Майлстоун может быть вложен в один другой (спринт в свой релиз), на один уровень, и тогда считает прогоны вложенных майлстоунов своими.

  • GET /api/milestones — все майлстоуны, сначала открытые и с ближайшим сроком, каждый с progress: runs, openRuns (ручные прогоны, которые никто не закрыл, и импортированные прогоны, которые ещё идут) и его результаты по вердиктам (results, passed, failed, blocked, skipped, untested).
  • GET /api/milestones/:key — { milestone, children, runs }: майлстоун, вложенные в него майлстоуны и прогоны, отнесённые к любому из них.
  • POST /api/milestones{ name, description?, parentKey?, startDate?, dueDate? } и PUT /api/milestones/:key с любым из этих полей (null очищает поле). Даты в формате YYYY-MM-DD; срок раньше даты начала, родитель, который сам вложен в другой, или майлстоун с вложенными майлстоунами, помещаемый в другой, получают 400. Нужно runs:write.
  • POST /api/milestones/:key/complete — майлстоун завершён и больше не принимает прогоны. Если остались открытые прогоны, ответ — 409 с их числом в openRuns, если только не передано force: true. POST /api/milestones/:key/reopen отменяет завершение.
  • DELETE /api/milestones/:key — его прогоны сохраняются и ни к чему не относятся; вложенные майлстоуны остаются сами по себе.
  • PATCH /api/runs/:id/milestone — { milestoneKey } относит прогон к майлстоуну или, при null, ни к какому. Прогон несёт milestoneKey и milestoneName; GET /api/runs/search?milestone=MS-3 выводит прогоны, отнесённые к нему или к вложенному в него майлстоуну. Перезапуск остаётся в майлстоуне своего прогона, пока тот открыт.

Экспорт отчётов и публичные ссылки ​

Эндпоинты /api/reports входят в модуль analytics (тариф Pro).

  • GET /api/reports/skills (runs:read) — навыки отчётов, по которым агент может написать отчёт; из них выбирает панель «Отчёты».
  • GET /api/reports/dashboard (runs:read) — дашборд проекта в виде закреплённых на нём разделов. POST /api/reports/dashboard/sections (agents:write) закрепляет раздел, а DELETE /api/reports/dashboard/sections/:index (agents:write) убирает раздел на этой позиции. Дашборд принадлежит проекту и общий для всех его участников, поэтому для закрепления нужно больше, чем runs:read.
  • POST /api/reports/generate возвращает отчёт как данные. Роль без models:use (наблюдатель) получает отчёты, макет которых задан навыком, без текстовых частей; всё, что модель написала бы или скомпоновала, получает 403. Панель «Отчёты» сохраняет отчёт как HTML, как книгу Excel (обзорный лист и по листу на раздел, числа — числами) или печатает его (сохранение в PDF в диалоге печати браузера).
  • POST /api/reports/shares { report, expiresInDays? (30, up to 90) } (нужно reports:share: лид, администратор) — ссылка только для чтения для людей без учётной записи. Страница рендерится на сервере из данных отчёта, с экранированием и без скриптов, и отдаётся по адресу /share/<token> с CSP, которая ничего не загружает, с noindex и без кеширования. url в ответе — единственный раз, когда выдаётся ссылка: хранится только хеш токена. Срок — 30 дней по умолчанию, не больше 90.
  • GET /api/reports/shares — ссылки проекта с числом просмотров (views).
  • DELETE /api/reports/shares/:id (reports:share) — сразу отзывает ссылку; это может сделать только её автор или администратор.

Истёкшая, отозванная или неизвестная ссылка отвечает одной и той же страницей 404. GET /share/:token — публичная страница без сессии; токен — единственные учётные данные.

Документация описывает последний выпуск Kvalli.