Skip to content

Тест-кейсы ​

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

Список и чтение ​

  • GET /api/testcases?limit=&offset= — кейсы проекта, сначала новые (created_at, затем номер кейса, так что страницы не повторяют и не пропускают кейсы), по одной странице: limit 1–1000 (по умолчанию 500), offset от 0 (по умолчанию 0). Тело ответа — по-прежнему простой массив кейсов; заголовок X-Total-Count говорит, сколько их в проекте, а пока остаются ещё, заголовок Link: </api/testcases?limit=500&offset=500>; rel="next" указывает следующую страницу (другие параметры запроса, например projectId, в нём сохраняются). limit или offset, который не является целым числом в допустимом диапазоне (0, 1001, -1, 1e2, пустое значение, параметр указан дважды), получает 400. Чтобы прочитать весь проект, следуйте по Link, пока он не исчезнет.
  • GET /api/testcases/search — одна страница совпадений (её используют экраны).
  • GET /api/testcases/stats — счётчики, которые показывает дашборд.
  • GET /api/testcases/:id — один кейс.

Создание, изменение, удаление ​

  • POST /api/testcases — создать кейс.
  • PUT /api/testcases/:id — изменить его; передайте version или If-Match, при несовпадении ответ — 409 с копией кейса на сервере.
  • DELETE /api/testcases/:id — удалить его (кейс попадает в корзину, см. Корзина).

Массовые операции ​

  • POST /api/testcases/bulk — создать много кейсов (импорт CSV, принятый результат агента).
  • POST /api/testcases/bulk-delete { ids } — удалить много кейсов, не больше 1000.
  • POST /api/testcases/bulk-update { ids, changes, versions? } — изменить много кейсов сразу (не больше 2000). changes может содержать priority, owner (пустое значение очищает его), muted вместе с muteReason, customField: { key, value? } (без значения поле очищается) и status. Изменение атрибутов сохраняет утверждение каждого кейса; каждый изменённый кейс получает ревизию. Для status нужно право cases:approve; кейс, уже находящийся в этом статусе, не трогается, а кейс, который нельзя перевести в этот статус, называется в refused. versions сопоставляет id кейса с версией, которую видел ревьюер: кейс, изменившийся с тех пор, отклоняется (с currentVersion), а не утверждается непросмотренным.
  • POST /api/testcases/tags { ids, add?, remove? } — изменить теги многих кейсов сразу, сохраняя их утверждение.

Утверждение ​

POST /api/testcases/:id/status { status, version? } переводит кейс между статусами draft, approved и deprecated (черновик, утверждён, устарел). Нужно право cases:approve (редактор, лид, администратор).

Утверждение собственной работы отклоняется с 403 для любой роли — то есть кейса, который написал вызывающий, или версии, в которой есть его правки с момента последнего утверждения, — если в проекте есть кто-то другой, кто может его утвердить. В команде без другого ревьюера автор может утвердить сам. GET /api/testcases/:id сообщает об этом в selfReview. Массовое утверждение называет такие кейсы в refused.

Для утверждения нужна version — версия, которую прочитал ревьюер (400 без неё, 409 с currentVersion, если кейс с тех пор изменился). Утверждение записывается к этой версии, поэтому последующая правка снова требует ревью.

История версий ​

  • GET /api/testcases/:id/revisions — сохранённые версии с разницей по полям (404 для несуществующего кейса), сначала новые, постранично: ?limit= (50 по умолчанию, не больше 200) и ?before= — значение nextBefore предыдущей страницы; на последней странице nextBefore нет.
  • POST /api/testcases/:id/revisions/:version/restore { version? } — записывает старую версию обратно как новую, а не переписывает историю. version — версия кейса на экране, целое число (400 иначе); если есть более новая правка, ответ — 409, как у PUT: { error, code: "version_conflict", currentVersion, serverCase }. Поля, удалённые с тех пор, пропускаются, удалённые с тех пор варианты значений сохраняются, а поле, ставшее обязательным, сохраняет текущее значение кейса.

Файлы, прогоны и требования кейса ​

  • GET|POST /api/testcases/:id/attachments, GET|DELETE /api/testcases/:id/attachments/:attachmentId — файлы кейса, до 25 МБ каждый, всегда отдаются как скачивание.
  • GET /api/testcases/:id/runs — прогоны, в которых тест заявил, что покрывает этот кейс.
  • GET /api/testcases/:id/requirements — что этот кейс должен проверять.

Изменившиеся требования ​

Кейс, который Агент 1 написал по задаче Jira или YouTrack или по странице Confluence, хранит sourceRef (jira:KVQA-2, youtrack:SHOP-12, confluence:98307) и sourceVersion (последнее изменение задачи, версия страницы). Каждые шесть часов сервер заново читает эти источники (не больше 200 за проход; источник, который не удалось прочитать, ничего не меняет) и помечает isStale кейсы, написанные по более старой версии, — без новой версии кейса, так что утверждённый кейс остаётся утверждённым. ?drift=true выводит такие кейсы. Источники опрашиваются, а не присылают изменения: Kvalli не принимает вебхуки от трекеров.

  • POST /api/testcases/drift/check (cases:write) — прочитать источники этого проекта сейчас; ответ { checked, markedStale, unreadable }.
  • GET /api/testcases/:id/source — источник в текущем виде: { ref, version, title, content, sourceType, label, caseVersion, changed } (content — то, по чему пишет Агент 1); 404 для кейса без такого источника, 502, если его не удалось прочитать.
  • POST /api/testcases/:id/source/accept (cases:write) — кейс сверен с текущим источником: пометка снимается, sourceVersion переносится на текущую версию; содержимое кейса и его утверждение не меняются.

Папки ​

Кейс несёт folderPath — путь к его папке от корня (["Checkout", "Payments", "Cards"], или текст "Checkout/Payments/Cards" на входе). Имя папки не может содержать /, вложенность — до 12 уровней. suite и feature — первые два уровня, они синхронизируются с путём; вызывающий, который передаёт только их, всё равно размещает кейс, сохраняя всё, что лежит ниже этих двух уровней.

  • GET /api/testcases/search?folder=Checkout/Payments — кейсы в папке и во всех вложенных папках.
  • GET /api/testcases/facets — folders: все папки с числом кейсов, лежащих непосредственно в них (у пустой папки count: 0).
  • POST /api/testcases/folders { path } — создать папку до того, как в неё что-то положено.
  • PUT /api/testcases/folders { path, newPath, merge? } — переименовать или переместить папку со всем содержимым. С существующей папкой она сливается только при merge: true; без него ответ — 409 с code: "folder_exists". Перемещённые кейсы получают новую версию и сохраняют утверждение. 400 при перемещении папки в саму себя, 404 для несуществующей папки.
  • DELETE /api/testcases/folders { path, deleteCases } — удалить папку и её подпапки; её кейсы поднимаются в родительскую папку (с верхнего уровня — в General), если deleteCases не равно true.
  • POST /api/testcases/move { ids, folderPath } — положить кейсы в папку.
  • PUT /api/testcases/suite/rename, DELETE /api/testcases/suite/delete, PUT /api/testcases/feature/rename, DELETE /api/testcases/feature/delete по-прежнему работают: suite — это папка верхнего уровня, feature — папка под ней.

Содержимое кейса ​

Кейс также несёт owner (ответственный) и muted с muteReason: падения заглушённого кейса записываются и учитываются, но не делают прогон красным.

  • preconditions — что должно выполняться до первого шага, отдельно от описания.
  • automationRef — автотест, которому соответствует кейс; см. Привязка результатов к кейсам.

Общие шаги ​

Общий шаг — блок шагов, который хранится один раз и используется во многих кейсах. Кейс использует его, добавляя { "sharedStep": "SS-3" } в свои steps. При чтении кейса шаги блока подставляются на место ссылки, каждый с shared: { key, title, instance }; при сохранении шаги, прочитанные так, снова превращаются в ссылку, так что прочитанный и сохранённый кейс остаётся со ссылкой на общий шаг. Ссылка на общий шаг, которого нет в проекте, получает 400.

В кейсе не больше 500 шагов, общие шаги используются в нём не больше 50 раз, а с развёрнутыми общими шагами получается не больше 1000 шагов. Шаг — не больше 2000 символов на действие и 2000 на ожидаемый результат. Больше — 400.

  • GET /api/shared-steps — все общие шаги с usedBy.
  • GET /api/shared-steps/:key — один общий шаг с кейсами (cases), которые его используют.
  • POST /api/shared-steps { title, steps } — создать.
  • PUT /api/shared-steps/:key { title?, steps?, version } — изменить его для всех кейсов, которые его используют (409, если он изменился после version; 400 с названием кейсов, если с более длинными шагами какой-то из них превысит 1000 шагов).
  • DELETE /api/shared-steps/:key — удалить его и развернуть его шаги на месте в кейсах, которые его использовали.

Ручной прогон копирует шаги в том виде, в каком они есть при его запуске, поэтому последующее изменение общего шага не переписывает то, что уже было выполнено.

Параметры ​

parameters — { names, rows }: данные, с которыми выполняется кейс, по строке на итерацию, например { "names": ["browser"], "rows": [["chrome"], ["firefox"]] }. Шаг, общий шаг, используемый кейсом, предусловия и критерии приёмки записывают значение как {{browser}}; плейсхолдер, которому не соответствует ни один параметр, остаётся как есть. Не больше 20 параметров и 100 строк; имена состоят из букв, цифр, _, - и . и должны различаться не только регистром. Пустые строки отбрасываются. null снимает параметры с кейса. CSV Kvalli хранит таблицу как JSON в столбце parameters; списки значений параметров в экспортах Qase читаются как все их комбинации.

Теги ​

tags — метки поверх папок (массив или текст, разделённый запятыми). Хранятся обрезанными по краям, каждая один раз без учёта регистра; не больше 30 тегов по 64 символа.

  • GET /api/testcases/search?tags=smoke,mobile — кейсы, у которых есть все названные теги.
  • GET /api/testcases/facets — список tags с их количеством.

Поля кейсов ​

customFields — значения собственных полей проекта, по ключу поля. Значения проверяются по определениям: ключ, которого нет ни у одного поля, или значение не того типа либо вне вариантов списка получает 400. Запрос, который передаёт customFields, должен заполнить все обязательные поля; запрос, который их не передаёт (агент, импорт), оставляет их как есть. Фильтр — ?cf.<key>=<value>; поле с множественным выбором совпадает по любому из своих значений.

  • GET /api/case-fields — поля проекта.
  • POST /api/case-fields { label, type, options?, required?, key? }, PUT /api/case-fields/:key { label?, options?, required?, position? } и DELETE /api/case-fields/:key — требуют settings:write. Типы: text, textarea, number, select, multiselect, checkbox, date (YYYY-MM-DD), url (http или https). Ключ и тип поля после создания не меняются; удаление поля удаляет его значение из всех кейсов.
  • GET /api/case-fields/:key/usage — { cases }: у скольких кейсов в этом поле есть значение.

В интерфейсе поля настраиваются в разделе «Настройки → Поля кейсов».

Поиск и похожие кейсы ​

  • POST /api/testcases/best-match { query } (cases:read) — «Упорядочить по совпадению»: кейсы проекта, содержащие слова запроса; сначала те, где есть вся фраза (в названии выше, чем в другом месте), затем по числу совпавших слов, у каждого — matchScore ({ results, matches }). Модель не используется, поэтому эндпоинт работает на любом тарифе, включая Starter. Пустой запрос возвращает 10 самых новых кейсов.
  • POST /api/testcases/similar { title, steps?, excludeId?, limit? (5) } — кейсы, наиболее похожие на тот, что сейчас пишется, чтобы поймать дубликат до сохранения. Сравнение идёт по значимым словам (без служебных слов и окончаний): название — 60%, шаги — 40%, если шаги есть с обеих сторон. У каждого ответа есть score (0–1, начиная с 0.5) и общие слова названия. Устаревшие кейсы и excludeId не учитываются. Редактор спрашивает подтверждение перед сохранением нового кейса, если его оценка относительно существующего — 0.8 или больше.

Сохранённые виды ​

GET|POST /api/saved-views, PUT /api/saved-views/:id, DELETE /api/saved-views/:id — сохранённые виды списка репозитория ({ name, shared, query: { folder, q, priority, review, owner, tag, fields, muted, drift } }).

Вид виден тому, кто его сохранил, а при shared — всему проекту. PUT принимает { query, version? } и меняет только сохранённый запрос; он запрещён (403) всем, кроме автора вида, включая обладателей settings:write. Удалить вид может его автор или пользователь с settings:write.

Импорт из других систем ​

POST /api/testcases/import{ content, format?, fileName?, intoFolder?, skipExisting?, dryRun?, testOpsProjectId?, folderFields? } — загрузить экспорт из другой системы: Qase JSON или CSV, выгрузку тест-кейсов Allure TestOps или TestRail в CSV либо собственный экспорт Kvalli в CSV или JSON. format: auto (по умолчанию, определяется по содержимому), kvalli-csv, kvalli-json, qase-json, qase-csv, testops-csv, testrail-csv.

Выгрузка TestRail (Test Cases → Export → CSV, все колонки):

  • папки — из Section Hierarchy (Regression Pack > Hosting > Отчёты);
  • шаблон «Test Case (Steps)»: шаги со строк под кейсом, каждый со своим ожидаемым результатом;
  • шаблон «Test Case (Text)»: шаги, пронумерованные вручную (1 Открыть, 2.Войти, 3) Проверить, строки с #), а нумерованный ожидаемый результат попадает в шаг с тем же номером; результат без своего шага остаётся общим ожидаемым результатом кейса, как записан;
  • шаблон «Checklist» — в описание;
  • предусловия, постусловия и References переносятся, Created By — автор, Type, Automation Type и собственные колонки TestRail заполняют поля проекта;
  • sourceRef — testrail:C4643, повторный импорт не задваивает кейсы;
  • картинок, которые хранит TestRail, в файле нет: на их месте стоит «[TestRail attachment …]».

Выгрузка Allure TestOps (Тест-кейсы → Экспорт → CSV) читается так же, как проект через API, и превращается в те же кейсы:

  • шаги — из колонки scenario с вложенностью и ожидаемыми результатами; статус, владелец (роль Owner), теги, задачи и ссылки, автотест (full_name) переносятся; остальные роли и собственные поля заполняют поля проекта, недостающие поля создаются; несколько значений поля в файле записаны через запятую;

  • папки — из полей folderFields (например ["Suite", "Project", "Feature"]), а без них — как при импорте через API без дерева команды;

  • testOpsProjectId — номер проекта в TestOps, которого в файле нет: с ним sourceRef такой же, как у импорта через API (testops:35:8120), и повторный импорт любым способом не задваивает кейсы;

  • проект больше KVALLI_MAX_IMPORT_CASES записывается частями;

  • одиночная кавычка, которую TestOps не экранирует (…'Got it"), читается как часть текста, а не как начало значения;

  • в файле нет вложений, общих шагов (они выписаны в каждом кейсе), деревьев команды и hash автотеста, а у ссылок — только их имена: всё это переносит импорт через API.

  • Разделы и наборы становятся папками, разделённые шаги сохраняют ожидаемые результаты, предусловия, постусловия и ссылки попадают в описание.

  • Каждый кейс сохраняет своё происхождение в sourceRef (qase:SHOP:45), поэтому при skipExisting (по умолчанию) повторный импорт того же файла добавляет только новое.

  • dryRun: true ничего не записывает и отвечает, что было бы импортировано, с примером. 400 называет, что не так с файлом, который не удалось прочитать.

  • Импортированные кейсы создаются черновиками, кроме тех, что в своей системе действуют (Active в Allure TestOps, actual в Qase): они приходят утверждёнными, если импортирующий может утверждать кейсы.

  • Лимит тела — 50 МБ, и не больше 4000 кейсов за один импорт (KVALLI_MAX_IMPORT_CASES): файл больше отклоняется с просьбой загрузить его частями, потому что весь импорт — одна транзакция, и сбой посередине ничего не оставляет.

  • Qase CSV проверен на примерах экспорта самого Qase, V1 и V2: экранированный текст шагов, список внутри неэкранированного текста шага V1 (остаётся в шаге), steps_data (сохраняется как «Data: …» под действием), вложенные шаги (нумерация 1.1.), is_muted (кейс приходит заглушённым), status (actual — утверждён, draft, deprecated) и parameters: строки имя:значение становятся таблицей параметров кейса, со всеми сочетаниями значений (групповые параметры Qase в файле выглядят так же, и импорт об этом предупреждает); текст другого вида остаётся в описании.

  • Столбцы, для которых в Kvalli нет поля (type, layer, behavior, automation, severity, status, milestone, is_flaky), заполняют поля проекта с тем же ключом, если значение подходит; сводка называет эти поля (mappedFields) и предупреждает о значениях, которые не подошли.

Импорт из Allure TestOps ​

/api/testcases/import/testops — перенос проекта из Allure TestOps, который читается через его API с персональным API-токеном (токен обменивается на JWT в /api/uaa/oauth/token и обменивается снова, если JWT истекает во время импорта). url и token можно не передавать — тогда используются сохранённые в настройках («Настройки → Импорт из TMS»).

  • POST /connection { url?, token? } — чей это токен и какие проекты он видит.
  • POST / { url?, token?, projectId, filter? } — начать чтение проекта (202, задача); filter — фильтр TestOps, например tag = "regression". Чтение требует запроса на каждый кейс и поле, поэтому идёт в фоне.
  • GET /jobs/:id — status (reading, ready, importing, done, failed, cancelled), startedAt, progress, а после чтения — число кейсов, общих шагов и файлов, собственные поля (fields), которые используют кейсы, и деревья проекта в TestOps (trees: name и fields). Запрос, сорвавшийся по пути (обрыв соединения, ответ шлюза 502, 503 или 504), повторяется дважды с паузой; неверный адрес, отказ в соединении, сертификат и таймаут не повторяются.
  • GET /jobs (GET /api/testcases/import/testops/jobs) — собственные задачи вызывающего в текущем проекте, сначала новые, каждая в том виде, в каком её возвращает GET /jobs/:id.
  • POST /jobs/:id/preview { intoFolder?, skipExisting?, folderFields?, createFields? } — что сделает импорт, по уже прочитанным данным. Папки строятся из folderFields, начиная с внешней; по умолчанию — дерево, которое команда создала в TestOps (кроме стандартных Features и Suites), а без него первое из Epic → Feature → Story, Parent Suite → Suite → Sub Suite, Component, что заполнено у кейсов. fieldsToCreate — поля проекта, которые импорт создаст.
  • POST /jobs/:id/commit { …, withAttachments?, createFields? } — записать (202; когда задача в статусе done, она сообщает result). Проект больше KVALLI_MAX_IMPORT_CASES записывается частями этого размера, каждая своей транзакцией; если импорт остановился после первой части, записанное остаётся, а повторный импорт добавляет остальное.
    • Шаги сохраняют ожидаемые результаты (узел ожидаемого результата TestOps) и вложенность (как отступы › ); шаг из формы сценария более старой версии тоже читается.
    • Общие шаги становятся общими шагами Kvalli, используемыми по ссылке, и объединяются с одноимённым общим шагом из предыдущего импорта; общий шаг, использованный внутри другого, разворачивается в нём. Общий шаг из одного шага, повторяющего его имя, становится этим шагом, а не именем со вложенным повтором.
    • Severity или Priority дают приоритет, участник Owner — ответственного, задачи и ссылки попадают в описание, остальные собственные поля заполняют одноимённые поля проекта, файлы (до 25 МБ) копируются. Полей, которых в проекте нет, импорт создаёт сам (createFields, по умолчанию да): выбор из значений, если их не больше 100, множественный — если у кейса их несколько, иначе текст.
    • Каждый кейс хранит sourceRef testops:<project>:<id>, поэтому повторный импорт добавляет только новое.
  • POST /history { url?, token?, projectId, days? } — перенести запуски проекта за последние days дней (по умолчанию 30, не больше 365) как прогоны (202, задача; нужны cases:write и runs:import). Прогон датируется временем запуска, результаты связываются с перенесёнными кейсами по id кейса TestOps, у упавших читается текст ошибки (до 200 на запуск). Уже перенесённые запуски пропускаются. Больше KVALLI_TESTOPS_MAX_LAUNCHES (3000) — берутся самые новые. GET /history/:id — ход задачи (progress, а в конце result: launches, created, skipped, results, warnings), DELETE /history/:id — остановить; уже перенесённые прогоны остаются.
  • DELETE /jobs/:id — остановить задачу. Задача хранится в памяти для запустившего её пользователя в течение часа после того, как она последний раз продвинулась или её смотрели; у одного пользователя не больше пяти задач, и запуск шестой вытесняет его самую старую.

Обсуждение и замечания ​

  • GET|POST /api/testcases/:id/comments ({ body }, нужно cases:write), PATCH /api/testcases/:id/comments/:commentId ({ body? , resolved? }) и DELETE — обсуждение кейса. Текст комментария меняет только автор; удалить комментарий может автор или ревьюер (cases:approve).
  • POST /api/testcases/:id/request-changes { body } (нужно cases:approve) — ревьюер возвращает кейс на доработку. Замечание сохраняется в обсуждении как kind: "changes_requested" к той версии кейса, на которой оно сделано, и остаётся открытым до разрешения (PATCH … { resolved: true }); утверждённый кейс возвращается в черновики. Утверждение кейса разрешает оставшиеся открытые замечания. Пока они есть, у кейса есть openRemarks.

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