Pasos y adjuntos en Vitest
Los pasos de una prueba se muestran bajo su resultado en Probara, anidados tal como se ejecutaron,
cada uno con su estado, su duración y su error. Vitest no tiene pasos propios: escríbelos con
probara.step(), que ejecuta su cuerpo y lo registra. Un resultado lleva también los archivos que
su intento adjuntó con probara.attach() y con el propio context.annotate() de Vitest, las
capturas de pantalla que Vitest toma de un fallo en modo navegador y, con captureOutput, la salida
de consola del intento.
probara.step()
Sección titulada «probara.step()»import { probara } from '@probara/vitest-reporter';import { expect, test } from 'vitest';
test('checks out', async () => { const cart = await probara.step('Open the cart', async () => ({ items: 2 })); await probara.step('Pay', async () => { probara.step('Enter the card', () => { expect(cart.items).toBe(2); }); await probara.step('Confirm', async () => { expect(await Promise.resolve('declined')).toBe('paid'); }); });});[ { "status": "failed", "steps": [ { "action": "Open the cart", "status": "passed" }, { "action": "Pay", "status": "failed", "steps": [ { "action": "Enter the card", "status": "passed" }, { "action": "Confirm", "status": "failed" } ] } ] }]probara.step(title, body, options) ejecuta body y devuelve lo que este devuelve, así que un paso
puede calcular un valor que use la prueba (cart, arriba):
| El cuerpo | El paso | Qué hace probara.step() |
|---|---|---|
| Devuelve un valor | passed | Devuelve su valor |
| Lanza un error | failed, con el mensaje y la traza del error | Lanza el mismo error: la prueba falla como siempre |
Devuelve una promesa (async) | Termina cuando la promesa se resuelve: passed, o failed | Devuelve una promesa que se resuelve igual |
| Devuelve otro thenable | Termina cuando vuelve el cuerpo (ver abajo) | Lo devuelve intacto |
Ninguno (probara.step('Seeded')) | passed, sin duración | Devuelve undefined |
- El anidamiento sigue a las llamadas: un paso que empieza dentro del cuerpo de otro es su
hijo, también a través de
await. Dos pasos del mismo padre ejecutados conPromise.allson ambos sus hijos. Un error que sale del cuerpo de un paso hace fallar el paso, y también cada paso padre del que sale. - Un paso que nunca terminó (una promesa que la prueba no esperó, todavía pendiente al acabar la
prueba) queda
failed, con el errorThe step had not finished when the test ended. Espera conawaitcadaprobara.step()que tenga un cuerpoasync. - Hooks: un paso en
beforeEachoafterEaches un paso de la prueba en curso. EnbeforeAll,afterAllo el cuerpo de undescribeno corre una sola prueba: el cuerpo se ejecuta, y el paso no se registra (dónde llamar a los helpers). - Reintentos: cada intento registra sus propios pasos; un reintento empieza sin ninguno (reintentos y repeticiones).
- Modo navegador: un navegador no tiene un contexto asíncrono que seguir, así que un paso solo
queda anidado bajo otro cuando empieza en la parte síncrona del cuerpo del otro. Un paso que
empieza después de un
awaiten un cuerpo no es hijo de ese paso.
Pasos de un caso nuevo
Sección titulada «Pasos de un caso nuevo»Los pasos de un resultado registran lo que se ejecutó. Los pasos de un caso son su
especificación: una acción, el resultado esperado y los datos de prueba. Los pasos más externos de
un intento, en el orden en que empezaron, son los pasos del caso que crea el informe; expected y
data van con ellos:
import { probara } from '@probara/vitest-reporter';import { test } from 'vitest';
test('refunds an order', () => { probara.step('Open the order', () => {}, { expected: 'The order is shown', data: 'order=1042' }); probara.step( 'Refund it', () => { probara.step('Confirm the dialog', () => {}); }, { expected: 'The order is refunded' }, );});[ { "steps": [ { "action": "Open the order", "expected": "The order is shown", "data": "order=1042" }, { "action": "Refund it", "expected": "The order is refunded", "steps": [{ "action": "Confirm the dialog" }] } ], "case": { "steps": [ { "action": "Open the order", "expected": "The order is shown", "data": "order=1042" }, { "action": "Refund it", "expected": "The order is refunded" } ] } }]- Los pasos anidados (
Confirm the dialog) son solo pasos del resultado: cómo funciona una prueba, no lo que especifica. - Como todos los campos del caso, los pasos del caso solo se aplican cuando el informe crea el caso (solo casos creados). Un caso existente conserva sus propios pasos.
expectedydatason cadenas: un paso que recibe otra cosa se registra solo con su título, y una advertencia lo indica.
Promesas y thenables
Sección titulada «Promesas y thenables»Un cuerpo que devuelve una promesa termina su paso cuando la promesa se resuelve. probara.step()
devuelve una promesa nueva que se resuelve igual, así que await probara.step(...) equivale a
esperar el cuerpo. Un rechazo que la prueba nunca gestiona sigue siendo un rechazo no gestionado,
que Vitest reporta, igual que sin el paso.
Un cuerpo también puede devolver un thenable que no es una promesa: una petición de
supertest, un constructor de consultas. Llamar a su then() lo iniciaría (o lo iniciaría dos
veces), así que probara.step() lo devuelve intacto, y el paso termina en cuanto vuelve el cuerpo,
antes de que se envíe la petición. Para medir la petición dentro del paso, espérala en un cuerpo
async:
import { probara } from '@probara/vitest-reporter';import { expect, test } from 'vitest';
// A stand-in for a supertest request: it runs when awaited.const request = () => ({ then: (resolve: (response: { status: number }) => void) => resolve({ status: 200 }),});
test('answers the health check', async () => { const response = await probara.step('Call /health', async () => await request()); expect(response.status).toBe(200);});[{ "status": "passed", "steps": [{ "action": "Call /health", "status": "passed" }] }]probara.attach()
Sección titulada «probara.attach()»Pásale un archivo en disco o un cuerpo, y espéralo con await. Funciona desde cualquier lugar en
el que corre una prueba: el cuerpo de la prueba, un hook beforeEach o afterEach,
onTestFinished, un paso, una función auxiliar o un page object que llama la prueba.
import { writeFileSync } from 'node:fs';import { tmpdir } from 'node:os';import { join } from 'node:path';import { probara } from '@probara/vitest-reporter';import { test } from 'vitest';
test('prints the receipt', async () => { const path = join(tmpdir(), 'receipt.txt'); writeFileSync(path, 'Total: 25.00'); await probara.attach({ name: 'receipt', path }); await probara.attach({ name: 'order', body: JSON.stringify({ id: 1042, total: 25 }), contentType: 'application/json', }); await probara.attach({ name: 'log', body: 'paid in 1.2 s' });});El resultado recibe tres archivos:
receipt.txt text/plainorder.json application/jsonlog.txt text/plain| Campo | Con path | Con body |
|---|---|---|
name | Obligatorio: el nombre en Probara; se añade la extensión del archivo si no tiene | Obligatorio: el nombre en Probara; se añade la extensión del tipo de contenido si no tiene |
path | El archivo, absoluto o relativo al directorio en el que se ejecuta vitest | — |
body | — | Una cadena, un Buffer o un Uint8Array, enviado tal cual |
contentType | Opcional: se deduce del nombre del archivo | Opcional: se deduce de la extensión de name; si no, text/plain para una cadena y application/octet-stream para bytes |
- Se copia al llamarlo. El archivo (o el cuerpo) se copia en ese momento, así que una prueba puede borrarlo o sobrescribirlo justo después: Probara recibe lo que había en la llamada.
- Espéralo con
await. En Node la copia se hace antes de que vuelva la llamada; en modo navegador termina cuando se resuelve la promesa, y una copia que termina cuando su prueba ya acabó no se adjunta, con una advertencia que diceAwait probara.attach() before the test ends(probara.attach()en un navegador). - Una cadena se envía tal cual, como texto UTF-8, nunca se decodifica: pasa un
Buffero unUint8Arraypara contenido binario. - Un archivo que no se puede adjuntar (no existe, es una carpeta, pesa más de 32 MiB) es una
advertencia, como
probara.attach() did not attach "receipt": it does not exist; la prueba continúa, y su resultado se envía. - Cada intento tiene sus propios archivos: una prueba reintentada sube los archivos de cada intento, cada uno a su propio resultado.
Archivos de un paso
Sección titulada «Archivos de un paso»Un archivo adjuntado mientras se ejecuta un probara.step(), desde el cuerpo del paso o desde
cualquier cosa que este llame, va a ese paso, y Probara lo muestra bajo el paso:
import { probara } from '@probara/vitest-reporter';import { test } from 'vitest';
test('uploads an avatar', async () => { await probara.step('Choose the file', async () => { await probara.attach({ name: 'avatar.png', body: new Uint8Array([137, 80, 78, 71]) }); });});avatar.png image/pngAnotaciones de Vitest
Sección titulada «Anotaciones de Vitest»Los archivos del context.annotate(message, attachment) de Vitest se envían con el intento que los
creó, con el mensaje de la anotación como nombre. El mensaje en sí no se envía:
probara.comment() escribe una nota.
import { test } from 'vitest';
test('exports the report', async ({ annotate }) => { await annotate('summary', { body: 'Exported 3 rows', bodyEncoding: 'utf-8' });});summary.txt text/plain| Vitest | Un body de texto | Contenido binario |
|---|---|---|
| 4.1.3 y posteriores, 5 | Pasa bodyEncoding: 'utf-8': sin él, Vitest marca el cuerpo como base64 | Un cuerpo Uint8Array con su contentType, o un path |
| 3.2, de 4.0 a 4.1.2 | Tal cual: todavía no hay bodyEncoding | Un path: Vitest 3 pierde algunos bytes de un cuerpo binario, y ese cuerpo se omite, con una advertencia |
Vitest copia un path en su attachmentsDir, y el archivo en Probara recibe el mensaje de la
anotación y la extensión de la ruta: annotate('screen', { path: 'shot.png' }) es screen.png.
Vitest rechaza annotate() cuando el cuerpo de la prueba ya terminó: en afterEach y
onTestFinished, usa probara.attach().
Capturas de pantalla de los fallos
Sección titulada «Capturas de pantalla de los fallos»En modo navegador, Vitest toma una captura
de pantalla cuando una prueba falla, y el reporter la adjunta a la ejecución fallida, como
image/png: la primera captura al primer intento fallido, y así sucesivamente, o todas las capturas
al último intento fallido cuando sus cantidades no coinciden. attachScreenshots: false
(PROBARA_ATTACH_SCREENSHOTS=false) las deja fuera. Una prueba que se ejecuta en Node no tiene
ninguna.
Salida de consola
Sección titulada «Salida de consola»captureOutput: true (PROBARA_CAPTURE_OUTPUT=true) adjunta lo que cada intento escribe en la
consola: console.log, console.info y console.debug como stdout.log, y console.warn y
console.error como stderr.log, text/plain, cada uno solo cuando tiene texto. Está desactivado
por defecto y no necesita nada más: ni archivo de setup ni cambios en las pruebas. Vitest sigue
imprimiendo la salida como siempre.
reporters: ['default', ['@probara/vitest-reporter', { captureOutput: true }]],import { beforeEach, test } from 'vitest';
beforeEach(() => { console.info('Seeding the cart');});
test('prints what it does', () => { console.log('Opening the cart'); console.error('Slow response: 2.1 s');});stdout.log text/plainstderr.log text/plainAquí stdout.log contiene Seeding the cart y Opening the cart, y stderr.log contiene
Slow response: 2.1 s: los hooks beforeEach y afterEach de una prueba escriben en su intento.
Qué recibe cada intento
Sección titulada «Qué recibe cada intento»| Salida | Adónde va |
|---|---|
El cuerpo de la prueba, sus hooks beforeEach y afterEach | El intento que la escribió |
| Cada intento de una prueba reintentada o repetida | Su propio stdout.log y stderr.log |
Una prueba test.concurrent, o una prueba en un describe.concurrent | Se deja fuera, con una advertencia |
| Una prueba reintentada cuyos intentos no se registraron | Se deja fuera, con una advertencia (ver cuándo se ejecutan los reintentos) |
El ámbito del módulo, el cuerpo de un describe, beforeAll, afterAll | No se adjunta: Vitest no la asigna a ninguna prueba |
- Las pruebas concurrentes se dejan fuera. Vitest asigna cada escritura a la prueba en curso en su worker, que no siempre es la que la escribió cuando varias pruebas se ejecutan a la vez: antes que adjuntar una escritura a la prueba equivocada, el reporter deja fuera la salida de las pruebas concurrentes, y dice cuántas.
- Los reintentos se separan por tiempo. Los archivos de un intento contienen lo que Vitest recibió desde su inicio hasta el inicio del intento siguiente. Vitest entrega al reporter las escrituras de una prueba por lotes, así que una escritura hecha justo cuando empieza un reintento puede acabar en el archivo del intento anterior.
reporters: ['default', ['@probara/vitest-reporter', { captureOutput: true }]],import { test } from 'vitest';
test.concurrent('finds boots', async () => { console.log('searching for boots');});
test('finds hats', () => { console.log('searching for hats');});Solo la prueba que se ejecuta sola recibe su salida, stdout.log, y el registro dice por qué la
otra no tiene ninguna:
$ npx vitest run[probara] The console output of 1 test is left out: Vitest attributes the writes of concurrent tests to the wrong test, and the attempts of a retried or repeated test without recorded attempts cannot be told apart (first seen in the run; repeats are logged at debug)[probara] Sending 2 results of 2 tests (2 passed, 0 failed, 0 skipped, 0 blocked)[probara] Recorded 2 results (2 new cases, 0 unmatched) in R-1 (closed): https://app.probara.net/projects/SHOP/runs/R-1[probara] Attached 1 file to results (0 skipped, 0 failed)silent y onConsoleLog
Sección titulada «silent y onConsoleLog»silent y onConsoleLog de Vitest deciden lo que muestra la terminal, no lo que reciben los
reporters: la salida que ocultan se adjunta igualmente. Para que la salida de una prueba no llegue a
Probara, deja captureOutput desactivado.
silent: true,reporters: ['default', ['@probara/vitest-reporter', { captureOutput: true }]],import { test } from 'vitest';
test('works quietly', () => { console.log('nothing shows in the terminal');});stdout.log text/plainLímites de la salida
Sección titulada «Límites de la salida»- 32 MiB por prueba y flujo. A partir de ahí, el flujo conserva lo que cabe y termina con la
línea
[probara] The output of this test was cut here: an attachment holds at most 32 MiB. La salida de una ejecución completa no tiene límite. - Se guarda en memoria. El reporter conserva la salida de las pruebas de la ejecución hasta que esta termina, cuando envía los resultados: una ejecución cuyas pruebas escriben mucho necesita la memoria para ello.
- Las ejecuciones con sharding la conservan. El blob de un shard contiene la salida de sus
pruebas, y
vitest --merge-reportsadjunta los mismos archivos que una ejecución sin sharding (sharding).
Límites y qué se omite
Sección titulada «Límites y qué se omite»| Límite | Qué pasa |
|---|---|
| 32 MiB por archivo o cuerpo | Uno mayor se omite, con una advertencia |
| 20 archivos por resultado | Los archivos a partir del vigésimo se omiten, con una advertencia |
| Imágenes de más de 10 MiB o de más de 8192 px de ancho o alto | Se omiten con una advertencia (Probara convierte las imágenes a WebP) |
| Ejecutables y scripts | Se omiten: Probara rechaza sus tipos de contenido |
| Un archivo que falta, que no se puede leer o vacío | Se omite con una advertencia |
| Un resultado que no registró nada | Sus archivos no se suben |
Las advertencias nombran el archivo, y el registro termina con cuántos archivos se adjuntaron, se
omitieron y fallaron. uploadAttachments: false (PROBARA_UPLOAD_ATTACHMENTS=false) no sube
ninguno. Los archivos se suben después de que su informe queda registrado, mientras la ejecución
sigue abierta, y después se cierra la ejecución.
Dónde se guardan las copias
Sección titulada «Dónde se guardan las copias»El reporter guarda sus copias en <attachmentsDir>/probara/<id>/, dentro del attachmentsDir de
Vitest (.vitest-attachments en Vitest 3 y 4, .vitest/attachments en Vitest 5, por defecto),
hasta que se envían:
| La ejecución | Sus copias |
|---|---|
Todos los reporters son de los que trae Vitest (default, verbose, junit…) o este | Se eliminan cuando se envía la ejecución |
Un shard (--shard), una ejecución con blob, o cualquier otro reporter | Se conservan: un blob puede nombrarlas, y una fusión las lee |
Resultados que no se pudieron enviar, con resultsFile | Se copian junto al archivo de resultados, en <name>-attachments/ |
- Registra el reporter por el nombre de su paquete,
'@probara/vitest-reporter'. Indicado por una ruta o como objeto, cuenta como otro reporter, y sus copias se conservan. - Lleva el
attachmentsDircon los blobs. El blob de un shard nombra los archivos que adjuntó por su ubicación en la máquina del shard: copia elattachmentsDirdel shard junto con su blob al job que ejecutavitest --merge-reports, en la misma ruta. Un archivo que la fusión no encuentra se omite con la advertenciaSkipped an attachment: its file is missing or unreadable, y su resultado se envía igualmente (lleva los blobs y los adjuntos). - Las copias propias de Vitest de las rutas de las anotaciones se quedan en
attachmentsDir: Vitest nunca las elimina, y el reporter tampoco. Elimina la carpeta cuando crezca.
Cuando faltan pasos o archivos
Sección titulada «Cuando faltan pasos o archivos»- Falta un paso. Solo
probara.step()crea pasos: las llamadas aexpecty las funciones auxiliares no. Un paso llamado fuera de una prueba (beforeAll, el cuerpo de undescribe) ejecuta su cuerpo y no registra nada, con una advertencia. - Los pasos quedan aplanados, o fallan con
The step had not finished. Un paso iniciado sinawaittermina después de la prueba: espera conawaitcadaprobara.step()cuyo cuerpo seaasync. - El caso no tiene pasos. Solo un caso que crea el informe recibe pasos. Un caso existente conserva los suyos.
- Falta un archivo. El registro nombra cada archivo que dejó fuera, y por qué (falta un adjunto). Un archivo adjuntado dentro de un paso va a ese paso, no al resultado.
Relacionado: Metadatos y enlaces · Configuración · Solución de problemas · Fuente: steps.md, attachments.md, output.md