Скрипты в Tetiva — это JavaScript, который выполняется до отправки запроса (Pre-request) и после получения ответа (Post-response). Pre-скрипт подставляет заголовок, пересчитывает подпись или кладёт значение в переменную окружения; post-скрипт читает ответ, сохраняет из него токен и проверяет результат через pm.test. Скрипты живут на вкладке Scripts запроса или коллекции, выполняются в песочнице без доступа к файлам и сети, а всё, что они напечатали, попадает во вкладку Tests рядом с ответом.
Где писать#
Вкладка Scripts есть у каждого запроса, а у коллекции — раздел «Скрипты» (пункт «Открыть коллекцию» в её контекстном меню). Внутри — две кнопки: «Перед запросом» и «После ответа», каждая со своим редактором. Оба редактора остаются смонтированными, поэтому история отмен не теряется при переключении.
У WebSocket-запроса кнопка одна и называется «Перед подключением»: скрипт выполняется перед рукопожатием, а фазы post-response там нет — см. WebSocket.
Редактор понимает pm. и подсказывает методы по мере набора, а {{var}} подсвечиваются и резолвятся так же, как в теле запроса: наведите курсор — увидите значение из активного окружения, секреты замаскированы.


Наследование: запрос → коллекция → родитель#
Скрипт запроса перекрывает скрипт коллекции целиком. Если поле у запроса пустое или в нём одни пустые строки, Tetiva идёт вверх по дереву коллекций и берёт первый непустой скрипт (глубина обхода — до 50 уровней). Pre и post наследуются независимо: post-скрипт может прийти от коллекции, пока pre-скрипт задан на самом запросе.
Общий для всей коллекции скрипт авторизации поэтому пишется один раз в корневой коллекции — и работает для всех вложенных запросов, пока запрос не переопределит его своим.
API pm.*#
Доступно в обеих фазах:
| Вызов | Что делает |
|---|---|
console.log(...) | Печатает в блок Console вкладки Tests; объект или массив выводится как JSON. warn, error, info пишут в тот же поток, уровней нет |
pm.environment.get(key) | Значение переменной или undefined |
pm.environment.set(key, value) | Задать переменную; значение приводится к строке |
pm.environment.unset(key) | Убрать переменную из набора этого запуска |
pm.collectionVariables.get/set/unset | То же, что pm.environment: работает с переменными активного окружения |
pm.variables.get/set/unset/has, replaceIn(text), toObject() | Переменные этого запуска: значение из pre-скрипта подставляется в {{переменные}} запроса и видно его post-скрипту, но в окружение не сохраняется. Если локального значения нет, get берёт его из окружения |
btoa(text), atob(base64) | Base64, как в браузере. atob понимает и base64url, и строку без = в конце, так что JSON.parse(atob(jwt.split('.')[1])) читает payload JWT |
pm.expect(value) | Утверждение chai 4.5, как в Postman. Вне pm.test проваленное утверждение становится ошибкой скрипта |
pm.request.method | Метод запроса |
pm.request.url | URL после подстановки переменных |
pm.request.protocol | http, grpc, graphql или websocket |
pm.request.headers.upsert({ key, value }) | Добавить заголовок или заменить существующий; регистр имени не важен, так что content-type заменит Content-Type |
pm.request.headers.add({ key, value }) | Добавить заголовок; если такой уже есть, у него появится второе значение |
pm.request.headers.remove(key) | Удалить заголовок, в каком бы регистре ни было записано имя |
pm.request доступен в обеих фазах, но headers.add, upsert и remove в post-скрипте ни на что не влияют: запрос уже ушёл, и его изменённую копию никто не читает.
Только для gRPC: pm.request.service, pm.request.grpcMethod, pm.request.message (тело в JSON), а также pm.request.metadata с методами get(key), set(key, value), remove(key) и toObject(). Метаданные приходят в скрипт копией: set меняет то, что уйдёт на сервер, но запись в коллекции остаётся нетронутой. Подробнее — gRPC.
Только для GraphQL: pm.request.graphqlVariables и pm.request.graphqlOperation. См. GraphQL.
Только в post-скрипте:
| Вызов | Что делает |
|---|---|
pm.response.code | Код ответа числом |
pm.response.status | Текст статуса, "OK", как в Postman; у gRPC — имя статуса |
pm.response.text() | Тело ответа строкой |
pm.response.json() | Тело, разобранное как JSON; при невалидном JSON возвращает undefined, а не бросает исключение |
pm.response.responseTime | Время ответа в миллисекундах |
pm.response.headers.get(name), pm.response.headers.has(name) | Значение заголовка ответа (имя без учёта регистра) или undefined; есть ли такой заголовок |
pm.response.to.have.status(...), .header(...), .body(...), .jsonBody(...), pm.response.to.be.json | Проверки ответа, как в Postman; status принимает код или текст статуса |
pm.response.to.be.ok, success, clientError, serverError, notFound и другие | Класс статуса; у gRPC ok и success означают статус 0 |
pm.test(name, fn) | Тест: fn без исключения — PASS. Проваленное утверждение — FAIL с его сообщением, как в Postman (AssertionError: expected 200 to equal 201); другая ошибка, например TypeError, — FAIL с номером строки |
У gRPC добавляются pm.response.statusText (имя статуса) и pm.response.metadata — обычный объект, где каждому ключу соответствует первое значение.
Утверждения пишутся на chai 4.5, как в Postman: pm.expect(x).to.equal(y), eql, include, property, lengthOf и остальные. Проверка через throw тоже работает.
Что происходит с переменными#
Значения, которые скрипт положил через pm.environment.set, после выполнения запроса записываются в активное окружение: существующие переменные обновляются, недостающие создаются как обычные (не секретные). Если активного окружения нет, изменения живут только внутри запуска. А pm.variables.set в окружение не попадает никогда: значение перекрывает переменную окружения с тем же именем только в этом запросе. Разбор приоритетов — на странице Окружения и переменные.
Заголовки, которые ставит сам скрипт, тоже проходят подстановку — во всех протоколах. Раньше {{переменные}} из скрипта уходили на сервер как есть, теперь они раскрываются перед отправкой, как и всё остальное.
Ограничения песочницы#
- Файловой системы и сети нет:
fetch,XMLHttpRequest,WebSocket,require,process,module,Buffer,__dirnameне определены. - Таймеров нет:
setTimeoutиsetIntervalотсутствуют.Promiseдоступен, но отложить код нечем. - Стандартные
JSONиMathна месте, как иbtoaсatob. pm.globalsиpm.sendRequestесть только для того, чтобы упасть с понятным сообщением:pm.globals is not supported in Tetiva; use pm.environmentиpm.sendRequest is not supported in Tetiva: scripts cannot make network calls.- Состояние не переживает запуск: каждый скрипт получает свежую машину, глобальные переменные из прошлого запуска не видны.
- На каждую фазу — 5 секунд. Скрипт, который не реагирует на остановку (классический случай — регулярное выражение с возвратами), остаётся доигрывать в фоне, но окно и остальные вкладки при этом не зависают.
- Ограничения памяти нет. Это стоит помнить, открывая чужую коллекцию.
Ошибка в скрипте#
Если pre-скрипт бросил исключение или не уложился в 5 секунд, запрос не уходит — как в Postman. В историю ничего не попадает, окружение остаётся прежним. Панель ответа показывает Request not sent: The pre-request script of this request failed: (или of collection “Payments”, если скрипт пришёл от коллекции), за ним текст ошибки со строкой и столбцом, подсказку, где её исправить, и строки Console, которые скрипт успел напечатать.
Ошибка в post-скрипте ответ не трогает. Тесты, прошедшие до ошибки, и вывод консоли остаются во вкладке Tests, а в Script Errors появляется Post-response: с текстом ошибки и местом вроде (line 5, column 6). Изменения окружения из этого скрипта не сохраняются.
Тесты и консоль#
Вкладка Tests рядом с ответом собирает три блока: Test Results с PASS/FAIL по каждому pm.test, Console с выводом обеих фаз (строки помечены [pre] и [post]) и Script Errors с фазой и текстом ошибки. Бейдж на вкладке показывает «пройдено/всего», а при одних лишь console-строках — точку. Подробнее про панель ответа — Просмотр ответа.
Примеры#
Pre-скрипт: подпись и заголовок трассировки.
pm.request.headers.upsert({ key: "X-Request-Id", value: String(Date.now()) })
var token = pm.environment.get("access_token")
if (!token) {
console.warn("access_token пуст — сначала выполните запрос логина")
}Post-скрипт: сохранить токен и проверить ответ.
var data = pm.response.json()
pm.test("статус 200", function () {
pm.response.to.have.status(200)
})
pm.test("токен пришёл", function () {
pm.expect(data).to.have.property("access_token")
})
if (data && data.access_token) {
pm.environment.set("access_token", data.access_token)
}Копирование cURL и скрипты#
Пункт Copy as cURL у HTTP-запроса прогоняет pre-скрипт в режиме dry-run: изменения заголовков и переменных попадают в команду, но не сохраняются в базу и не создают запись в истории. Если скрипт упал, команда не копируется: уведомление называет ошибку, а его кнопка Generate code… копирует команду без запуска скрипта. За токеном OAuth 2.0 команда в сеть не ходит — если токена нет, она уйдёт без Authorization и с предупреждением. Остальные пункты Copy as, Copy as cURL у GraphQL-запроса и диалог Generate code скрипт не запускают и предупреждают об этом. Подробности — Импорт и экспорт.
Скрипты, написанные для Postman, могут потребовать правок: поддержано только подмножество pm.* из таблиц выше, и pm.sendRequest, pm.globals или require придётся заменить: скрипт, который их вызывает, падает на этой строке, а в pre-скрипте это ещё и останавливает отправку. Что переносится при миграции, разобрано на странице Переход с Postman.