Ir al contenido

Metadatos y enlaces de pruebas de Vitest

probara da al reporter más información sobre una prueba, desde el cuerpo de la prueba, sus hooks beforeEach y afterEach, o onTestFinished. Impórtalo del paquete del reporter:

tests/payments.test.ts
import { probara } from '@probara/vitest-reporter';
import { test } from 'vitest';
test('pays with a card', () => {
probara.title('Pays with a saved card').suite(['Payments', 'Cards']).tags('smoke');
});

No hay nada que configurar: el reporter añade lo que necesitan los helpers a cada proyecto de Vitest, en los pools threads y forks y en modo navegador.

HelperQué haceLlamadas repetidas
id(id | ids)Vincula casos existentes (vincular)Se suman
title(text)Título del caso que crea el informeGana la última
suite(title | titles)Ruta de suites del caso que crea el informeGana la última
comment(text)Un comentario, al principio de las notas del resultado, antes del errorGana la última
ignore()Deja fuera este intento: no se reporta—
tags(...names)Etiquetas del caso que crea el informeSe suman, cada una una vez
fields(record)Campos del caso que crea el informe: campos del sistema, campos personalizados, descriptionSe combinan por nombre
parameters(record)Parámetros del resultado, que se muestran con élSe combinan por nombre
link(url, name?), issue(id)Enlaces del resultado (enlaces e incidencias)Se suman
attach({ name, path | body, ... })Adjunta un archivo; espéralo con await (adjuntos)Cada una adjunta
step(title, body?, options?)Un paso del resultado, y del caso nuevo (pasos)Cada una es un paso

Los helpers que no devuelven otra cosa devuelven probara, así que las llamadas se encadenan. Cada una se aplica solo al intento en curso: un reintento empieza vacío. Los nombres, los argumentos y las reglas son los del reporter de Jest; diferencias con la API de Jest recoge lo que cambia Vitest.

title, suite, tags, fields y los pasos del caso describen el caso que crea un informe, la primera vez que se reporta una prueba. Un caso existente nunca cambia: Probara conserva lo que escribieron las personas. La clave de automatización tampoco cambia nunca (claves), así que un título nuevo sigue encontrando el mismo caso.

tests/payments.test.ts
import { probara } from '@probara/vitest-reporter';
import { test } from 'vitest';
test('pays', () => {
probara
.title('Pays with a saved card')
.suite(['Payments', 'Cards'])
.tags('smoke', 'payments')
.fields({
description: 'A returning customer pays with the card saved in their profile.',
severity: 'critical',
priority: 'high',
'Risk area': 'Checkout',
});
});
[
{
"automationKey": "tests/payments.test.ts > pays",
"title": "Pays with a saved card",
"suitePath": ["Payments", "Cards"],
"case": {
"description": "A returning customer pays with the card saved in their profile.",
"tags": ["smoke", "payments"],
"fields": { "severity": "critical", "priority": "high", "Risk area": "Checkout" }
}
}
]

fields() recibe nombres y valores (los números y los booleanos se convierten en cadenas):

NombreQué define
descriptionLa descripción del caso
severity, priority, type, layer, behavior, status, is_flakyLos campos del sistema, por el nombre de una opción (high)
preconditions, postconditionsLos campos de texto del sistema
Cualquier otro nombre, como 'Risk area'El campo personalizado con ese nombre

Probara resuelve los nombres y las opciones al crear el caso. Un campo o una opción que no conoce se omite y se registra como advertencia; el resultado se registra igualmente. Un campo con un valor vacío se omite sin aviso.

El título de un caso creado es el título de la prueba (sin los IDs de caso), salvo que title() indique otro. Su suite es el archivo de prueba (tests/payments.test.ts) o, con keyIncludesFile: false, el describe más externo, salvo que suite() indique una: suite('Payments') o suite(['Payments', 'Cards']) para suites anidadas, que se crean según haga falta bajo suiteUlid.

Las etiquetas de una prueba (Vitest 4.1 y posteriores) se suman a las etiquetas del caso que crea el informe, después de las de probara.tags(), cada una una vez. Vitest rechaza una etiqueta que su configuración no declara, así que declara cada una en test.tags:

// vitest.config.mts
import { defineConfig } from 'vitest/config';
export default defineConfig({
test: {
tags: [{ name: 'smoke' }],
reporters: ['default', '@probara/vitest-reporter'],
},
});
tests/search.test.ts
import { probara } from '@probara/vitest-reporter';
import { test } from 'vitest';
test('finds a product', { tags: ['smoke'] }, () => {
probara.tags('search');
});
[
{
"automationKey": "tests/search.test.ts > finds a product",
"case": { "tags": ["search", "smoke"] }
}
]

Como las etiquetas de probara.tags(), solo van a un caso creado: nunca a un resultado, nunca a la clave de automatización, así que añadir una etiqueta a una prueba nunca la mueve a otro caso.

comment() escribe una línea al principio de las notas del resultado, antes del error y su traza:

tests/search.test.ts
import { probara } from '@probara/vitest-reporter';
import { test } from 'vitest';
test('searches the catalog', () => {
probara.comment('Ran against the staging catalog');
});
[
{
"automationKey": "tests/search.test.ts > searches the catalog",
"notes": "Ran against the staging catalog"
}
]

Cuando el intento falla, sus errores siguen al comentario en las mismas notas, sin los colores de la terminal: el comentario nunca oculta un error. Los mensajes del propio context.annotate() de Vitest no se envían como notas: comment() es la forma de escribir una.

parameters() registra los valores con los que se ejecutó un intento, que se muestran con su resultado, como el usuario o el conjunto de datos de una prueba guiada por datos. Nunca forman parte de la clave: la prueba es un solo caso sean cuales sean sus parámetros. Cuando el caso declara un parámetro con el mismo nombre, el resultado se registra en el caso de ejecución con esos valores. Un parámetro que el caso no declara se queda en el resultado solo para mostrarlo. El proyecto de Vitest, attempt y repeat son contexto: se muestran en el resultado y nunca se convierten en un parámetro de un caso que un reporte crea.

tests/users.test.ts
import { probara } from '@probara/vitest-reporter';
import { test } from 'vitest';
test.each(['admin', 'viewer'])('opens the dashboard as %s', (role) => {
probara.parameters({ role, seats: 5 });
});
[
{
"automationKey": "tests/users.test.ts > opens the dashboard as admin",
"parameters": { "role": "admin", "seats": "5" }
},
{
"automationKey": "tests/users.test.ts > opens the dashboard as viewer",
"parameters": { "role": "viewer", "seats": "5" }
}
]
  • Las filas de test.each y test.for son pruebas propias, con la clave del título que Vitest les da: mantén un marcador en el título (%s, $role), para que cada fila sea un caso. Vitest no guarda los valores de una fila en ningún otro sitio, así que parameters() es la forma de enviarlos.
  • describe.each: llama a los helpers en cada prueba o en su beforeEach; una llamada en el cuerpo del describe se ejecuta mientras no corre ninguna prueba.
  • Dos pruebas con el mismo nombre completo comparten una clave, es decir, un caso: cada resultado conserva lo que dijeron sus propios helpers.
  • Los parámetros propios del reporter: attempt y repeat distinguen las ejecuciones de una prueba reintentada o repetida. Son contexto: se muestran en el resultado y nunca se convierten en un parámetro de un caso que un reporte crea. (reintentos y repeticiones), y project nombra el proyecto de Vitest de un resultado cuando la configuración tiene más de uno. Un project tuyo gana al de Vitest; attempt y repeat ganan a los tuyos.

ignore() deja fuera el intento en curso: sigue ejecutándose, y Vitest lo reporta, pero Probara no lo recibe. El recuento del registro dice cuántos se dejaron fuera.

tests/experimental.test.ts
import { probara } from '@probara/vitest-reporter';
import { test } from 'vitest';
test('tries the new checkout', () => {
probara.ignore();
});
test('pays', () => {});
$ npx vitest run
[probara] Sending 1 result of 1 test (1 passed, 0 failed, 0 skipped, 0 blocked); 1 ignored with probara.ignore()
[probara] Recorded 1 result (1 new case, 0 unmatched) in R-1 (closed): https://app.probara.net/projects/SHOP/runs/R-1

Un resultado puede llevar enlaces, que se muestran con él en Probara: el build que lo ejecutó, la página que probó, las incidencias de tu gestor a las que se refiere. probara.link() añade un enlace; probara.issue() nombra una incidencia, que issueUrlTemplate convierte en un enlace.

probara.link(url, name?) recibe una URL absoluta http o https y un nombre opcional, que se muestra en lugar de la URL:

tests/search.test.ts
import { probara } from '@probara/vitest-reporter';
import { test } from 'vitest';
test('searches the catalog', () => {
probara
.link('https://shop.example.com/search?q=boots', 'Search page')
.link('https://ci.example.com/builds/1042');
});
[
{
"automationKey": "tests/search.test.ts > searches the catalog",
"links": [
{ "url": "https://shop.example.com/search?q=boots", "name": "Search page" },
{ "url": "https://ci.example.com/builds/1042" }
]
}
]

probara.issue(id) nombra una incidencia de tu gestor por su ID. Con issueUrlTemplate (PROBARA_ISSUE_URL_TEMPLATE), una URL con %s donde va el ID, se convierte en un enlace del resultado, con el ID como nombre:

reporters: [
'default',
['@probara/vitest-reporter', { issueUrlTemplate: 'https://jira.example.com/browse/%s' }],
],
tests/checkout.test.ts
import { probara } from '@probara/vitest-reporter';
import { test } from 'vitest';
test('rounds the total', () => {
probara.issue('PAY-7').issue('PAY 12');
});
[
{
"automationKey": "tests/checkout.test.ts > rounds the total",
"links": [
{ "url": "https://jira.example.com/browse/PAY-7", "name": "PAY-7" },
{ "url": "https://jira.example.com/browse/PAY%2012", "name": "PAY 12" }
]
}
]
  • El ID entra en la plantilla codificado para URL (PAY 12 se convierte en PAY%2012), en lugar del primer %s.
  • La plantilla es una URL http o https con %s; cualquier otro valor desactiva el envío con un error que nombra issueUrlTemplate.
  • Sin plantilla, las incidencias se omiten, y una advertencia lo indica: el resultado se registra sin ellas.
tests/checkout.test.ts
import { probara } from '@probara/vitest-reporter';
import { test } from 'vitest';
test('rounds the total', () => {
probara.issue('PAY-7');
});
$ npx vitest run
[probara] Dropped the issues of probara.issue(): no issueUrlTemplate turns their ids into links (first seen in the run; repeats are logged at debug)
[probara] Sending 1 result of 1 test (1 passed, 0 failed, 0 skipped, 0 blocked)
[probara] Recorded 1 result (1 new case, 0 unmatched) in R-1 (closed): https://app.probara.net/projects/SHOP/runs/R-1
ReglaQué pasa
Enlaces e incidencias de un intentoSe envían en el orden de las llamadas, todos juntos
Una URL que no es una URL http/https absolutaSe omite, con una advertencia
Una URL de más de 2048 caracteresSe omite, con una advertencia
Un nombre de más de 255 caracteresSe recorta a 255 caracteres, con una advertencia
Más de 20 enlaces en un resultadoSe envían los 20 primeros, con una advertencia
Un reintentoEmpieza sin enlaces: cada intento envía los suyos

Los enlaces pertenecen al resultado, no al caso: un caso existente nunca cambia, y cada resultado conserva los enlaces que añadió su intento. Para vincular el propio caso a una incidencia de Jira, consulta Trazabilidad con Jira.

DóndeQué pasa
El cuerpo de la prueba, beforeEach, afterEach, onTestFinishedSe aplica al intento en curso
Una función auxiliar o un módulo que llama la pruebaLo mismo: los helpers encuentran por sí solos la prueba en curso
Una prueba test.concurrentSe aplica a esa prueba
El ámbito del módulo, el cuerpo de un describe, beforeAll, afterAllSe ignora, con una advertencia: ahí no corre una sola prueba

Un helper nunca lanza errores dentro de la prueba. Una llamada mientras no corre ninguna prueba es una advertencia por helper y archivo de prueba, que el archivo de prueba imprime en la salida de Vitest; un argumento incorrecto (aquí, una etiqueta vacía) es una advertencia del registro del reporter para la ejecución. En ambos casos, lo incorrecto se ignora y la prueba continúa:

tests/orders.test.ts
import { probara } from '@probara/vitest-reporter';
import { beforeAll, test } from 'vitest';
beforeAll(() => {
probara.tags('orders');
});
test('lists the orders', () => {
probara.tags('orders', '');
});
$ npx vitest run
[probara] probara.tags() only works while a test runs (in a test, or a beforeEach or afterEach hook)
[probara] probara.tags() ignores blank tags (first seen in the run; repeats are logged at debug)
[probara] Sending 1 result of 1 test (1 passed, 0 failed, 0 skipped, 0 blocked)
[probara] Recorded 1 result (1 new case, 0 unmatched) in R-1 (closed): https://app.probara.net/projects/SHOP/runs/R-1

probara.* tiene los nombres, los argumentos, las reglas de combinación y los mensajes del reporter de Jest. Vitest cambia lo siguiente:

En el reporter de VitestPor qué
No va nada en la configuración aparte del reporter: ni setupFilesAfterEnv, ni siquiera para captureOutput o runCasesOnlyVitest permite que un reporter añada su archivo de setup y su runner a cada proyecto antes de la ejecución
Espera probara.attach() con await. En Node el archivo se copia en la llamada, como en Jest; en modo navegador la copia termina después, y una que termina cuando su prueba ya acabó no se adjunta, con una advertencia que dice Await probara.attach() before the test endsEn un navegador, el lado Node de Vitest lee el archivo después de que la llamada vuelve
Una llamada de test.concurrent va a su propia prueba a través del runner del reporter. Donde ese runner no puede ejecutarse (modo navegador, un runner propio que Node no puede importar, como un archivo .jsx), se descarta con una advertenciaLa prueba en curso del propio Vitest es una por worker, así que puede nombrar la prueba concurrente equivocada: una llamada se descarta antes que registrarse en otra prueba
Una llamada fuera de una prueba avisa una vez por helper y archivo de prueba, en la salida del archivo de prueba (stderr | tests/orders.test.ts), no en el registro del reporterLa llamada se ejecuta en un worker de Vitest, que no puede escribir en el registro del reporter del proceso principal
Un argumento incorrecto en una prueba avisa una vez por ejecución, (first seen in the run; ...)La advertencia viaja con el resultado de la prueba, y el reporter registra cada una una sola vez
Las etiquetas de Vitest (4.1 y posteriores) se suman a las etiquetas de un caso creadoJest no tiene etiquetas de prueba
Una fila de test.each o test.for toma la clave del título que formatea Vitest, que Vitest 4 y 5 escriben distinto para los valores de tipo objetoLa clave es el nombre que da a la prueba la salida JUnit de Vitest, y ese nombre sale del formato del propio Vitest
Dos pruebas con el mismo nombre completo conservan cada una sus propias llamadas a los helpers (el reporter de Jest las deja fuera, con una advertencia)Vitest guarda las llamadas en los metadatos propios de cada prueba, así que no hay que distinguir dos nombres
Un shard ejecutado con --reporter=blob solo en la línea de comandos no registra ninguna llamada a los helpers: nombra el reporter junto a él (el reporter va junto a blob)--reporter sustituye los reporters de la configuración, así que el reporter nunca arranca y no añade ningún archivo de setup
Con el envío desactivado, los helpers siguen registrando, y no se envía nadaEl reporter añade su archivo de setup sea cual sea la configuración de envío, así que las pruebas se ejecutan igual con y sin token
En modo navegador los helpers funcionan en el navegador, y probara.attach({ path }) solo copia un archivo que Vite sirveUn navegador no puede leer un archivo: lo lee el lado Node de Vitest, con las reglas de server.fs de Vite

Vitest ejecuta las pruebas en workers y el reporter en su proceso principal. Cada llamada probara.* se escribe en la entrada del intento en curso dentro de los metadatos propios de la prueba (task.meta.probara), que Vitest envía al reporter con el resultado de la prueba; el reporter los lee cuando termina la ejecución. probara.attach() copia su archivo en ese momento a una carpeta dentro del attachmentsDir de Vitest. El blob de Vitest guarda los metadatos de cada prueba, así que el blob de un shard lleva las llamadas hasta vitest --merge-reports. Sin el reporter entre los reporters de la ejecución, cada helper no hace nada (y probara.step() sigue ejecutando su cuerpo), así que las pruebas siguen pasando donde el reporter no está registrado.

Límites conocidos:

  • task.meta.probara es del reporter. El código de prueba puede verlo (context.task.meta.probara), pero su forma no es una API, y escribir en él cambia lo que se envía: guarda tus propios metadatos bajo otra clave.
  • Un runner propio que Node no puede importar (.jsx, .tsx, o .ts en un Node sin eliminación de tipos) se deja tal cual, con una advertencia que lo nombra: sus intentos se siguen registrando, pero las llamadas de sus pruebas test.concurrent se descartan, con una advertencia.

Relacionado: Pasos y adjuntos · Vincular · Migrar desde Qase · Fuente: metadata.md, links.md