# Migrar al reporter de Vitest

> Pasa a @probara/vitest-reporter desde la importación JUnit, vitest-qase-reporter, Allure, ReportPortal o el trcli de TestRail conservando tus casos — y cómo actualizar el reporter y Vitest con seguridad.

Vengas de donde vengas, el reporter es una entrada en `reporters`. No va nada en `setupFiles` ni en
`runner`: el reporter añade su propio archivo de setup y su runner a cada proyecto de Vitest, en
Node y en modo navegador.

```ts
// vitest.config.mts
import { defineConfig } from 'vitest/config';

export default defineConfig({
  test: {
    reporters: ['default', ['@probara/vitest-reporter', { projectId: 'SHOP' }]],
  },
});
```

El reporter envía lo que envían las integraciones con Vitest de Qase, Allure, ReportPortal y
TestRail, siempre que Probara tenga un lugar para ello: vínculos con casos, el título, la suite, los
campos, las etiquetas y los pasos de un caso nuevo, parámetros, comentarios, pasos anidados,
archivos, enlaces, y el nombre, el plan, el entorno y las configuraciones de la ejecución. Además
envía cada intento de una prueba reintentada o repetida, cosa que ninguna de ellas hace, y mantiene
una sola ejecución para toda una sesión watch. Lo que no porta está en
[lo que el reporter no admite](#lo-que-el-reporter-no-admite), cada cosa con su motivo.

| Vienes de                                          | Ve a                                                |
| -------------------------------------------------- | --------------------------------------------------- |
| La salida JUnit de Vitest y `probara import junit` | [La importación JUnit](#desde-la-importación-junit) |
| `vitest-qase-reporter`                             | [Qase](#desde-qase)                                 |
| `allure-vitest`                                    | [Allure](#desde-allure)                             |
| `@reportportal/agent-js-vitest`                    | [ReportPortal](#desde-reportportal)                 |
| La salida JUnit de Vitest y `trcli parse_junit`    | [TestRail](#desde-testrail)                         |

## Desde la importación JUnit

Si tu pipeline escribe la salida JUnit de Vitest y la envía con `probara import junit`, el reporter
puede sustituir ambos pasos **sin desvincular un solo caso**: da a cada prueba la clave de
automatización y el caso que le da la importación, en Vitest 3, 4 y 5. Después, cada prueba envía
lo que el XML no puede contener: cada intento, los pasos, los archivos, los parámetros y las
llamadas `probara.*`. El reporter es el camino recomendado; conserva la importación para un job que
solo tiene el XML.

1. Añade el reporter a la configuración de Vitest
   ([primeros pasos](https://docs.probara.net/es/developers/ci-integrations/vitest/#3-añádelo-a-la-configuración-de-vitest)).
   No hay nada más que definir: la salida JUnit de Vitest siempre nombra el archivo de prueba, igual
   que las claves por defecto del reporter (`keyIncludesFile: true`).
2. [Comprueba las claves](#comprueba-las-claves-antes-de-cambiar) de ambos sobre las mismas pruebas:
   deben ser iguales.
3. En CI, quita `--reporter=junit` y el paso de importación, y pasa `PROBARA_API_TOKEN` al paso de
   pruebas ([en CI](#en-ci)).

| Salida JUnit de Vitest                                 | Ajuste del reporter                                                                                                |
| ------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------ |
| `--reporter=junit`, con o sin `addFileAttribute: true` | Nada: `keyIncludesFile: true` es el valor por defecto                                                              |
| Escrita con otro `ancestorSeparator` (Vitest 5)        | Claves distintas: vincula las pruebas por IDs de caso ([vincular](https://docs.probara.net/es/developers/ci-integrations/vitest/linking/)) |

Ejecuta `vitest` desde la misma raíz que antes: el archivo de cada clave es relativo a ella, en los
dos caminos. `keyIncludesFile: false` no tiene equivalente en JUnit: da las claves de casos creados
a mano o por otra herramienta, sin el archivo
([sin el archivo](https://docs.probara.net/es/developers/ci-integrations/vitest/linking/#sin-el-archivo)).

### Comprueba las claves antes de cambiar

Ejecuta la batería una vez con la salida JUnit de Vitest y otra con el
[archivo de resultados](https://docs.probara.net/es/developers/ci-integrations/vitest/results-and-retries/#archivo-de-resultados)
del reporter, con el envío desactivado, y compara las simulaciones de ambos: cada una imprime una
línea por resultado, con su estado, su caso y su clave. No hace falta token. Para este archivo de
prueba:

```ts
// tests/refund.test.ts
import { describe, expect, test } from 'vitest';

describe('refund', () => {
  test('SHOP-12 refunds an order', () => {
    expect(25 - 25).toBe(0);
  });

  test('keeps the receipt', () => {
    expect('receipt').toHaveLength(7);
  });

  test.todo('refunds a gift card');
});
```

```text
$ npx vitest run --reporter=junit --outputFile=reports/vitest.xml
$ PROBARA_PROJECT=SHOP npx @probara/cli import junit reports/vitest.xml --dry-run
passed	SHOP-12	tests/refund.test.ts > refund > refunds an order
passed	-	tests/refund.test.ts > refund > keeps the receipt
skipped	-	tests/refund.test.ts > refund > refunds a gift card
Total: 3 results from 1 file (2 passed, 0 failed, 1 skipped, 0 blocked)
$ PROBARA_ENABLED=false PROBARA_PROJECT=SHOP PROBARA_RESULTS_FILE=probara-results.json npx vitest run
[probara] Wrote 3 results to /work/shop/probara-results.json: send them with probara import results /work/shop/probara-results.json
$ npx @probara/cli import results probara-results.json --dry-run
passed	SHOP-12	tests/refund.test.ts > refund > refunds an order
passed	-	tests/refund.test.ts > refund > keeps the receipt
skipped	-	tests/refund.test.ts > refund > refunds a gift card
Total: 3 results from 1 file (2 passed, 0 failed, 1 skipped, 0 blocked)
```

Las líneas son iguales: las mismas claves, los mismos casos y los mismos estados. `--reporter=junit`
sustituye los reporters de la configuración en esa ejecución, así que entonces el reporter no envía
nada. En una batería real, escribe cada simulación en un archivo y compáralos con `diff`: una línea
cuya clave cambió es una prueba que recibiría un caso nuevo
([comprobar qué se enviaría](https://docs.probara.net/es/developers/ci-integrations/vitest/troubleshooting/#comprobar-qué-se-enviaría)).
Elimina `reports/` y el archivo de resultados al terminar.

### En CI

Antes, dos pasos: Vitest escribe el informe, y la CLI lo envía.

```yaml
- name: Run the tests
  run: npx vitest run --reporter=junit --outputFile=reports/vitest.xml
- name: Send the results
  if: ${{ !cancelled() }}
  run: npx @probara/cli import junit reports/vitest.xml
  env:
    PROBARA_API_TOKEN: ${{ secrets.PROBARA_API_TOKEN }}
    PROBARA_PROJECT: SHOP
```

Después, un solo paso: el reporter, registrado en la configuración de Vitest, envía cada resultado
cuando termina la ejecución.

```yaml
- name: Run the tests
  run: npx vitest run
  env:
    PROBARA_API_TOKEN: ${{ secrets.PROBARA_API_TOKEN }}
    PROBARA_PROJECT: SHOP
```

Quita `--reporter=junit` del comando: sustituye los reporters de la configuración, incluido el del
reporter. Si otra herramienta sigue leyendo el XML (un resumen de pruebas del CI), lista en cambio
`junit` junto al reporter:

```ts
reporters: ['default', 'junit', '@probara/vitest-reporter'],
outputFile: { junit: 'reports/vitest.xml' },
```

La ejecución recibe el nombre, el origen y el cierre que le daba la importación
([la ejecución](https://docs.probara.net/es/developers/ci-integrations/vitest/results-and-retries/#la-ejecución)).

### Qué cambia respecto a la importación JUnit

Las claves se mantienen; lo que envía cada prueba crece, y algunos resultados son más exactos:

| Tema                              | `probara import junit` sobre la salida JUnit de Vitest                                                                                           | El reporter                                                                                                                                                                                                         |
| --------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Reintentos (`retry`)              | Un resultado por prueba: un aprobado en un reintento es un aprobado sin más; una prueba que falla en todos los intentos conserva su primer fallo | Cada intento, en orden, cada uno con sus propios errores; el caso de la ejecución conserva el último resultado ([reintentos](https://docs.probara.net/es/developers/ci-integrations/vitest/results-and-retries/#reintentos-y-repeticiones)) |
| Repeticiones (`repeats`)          | Un resultado por prueba                                                                                                                          | Cada ejecución ([repeticiones](https://docs.probara.net/es/developers/ci-integrations/vitest/results-and-retries/#repeticiones))                                                                                                            |
| `test.todo`                       | `skipped`                                                                                                                                        | `skipped`, con la nota `Todo` ([estados](https://docs.probara.net/es/developers/ci-integrations/vitest/results-and-retries/#estados))                                                                                                       |
| `context.skip('why')`             | `skipped`: Vitest no escribe ningún motivo                                                                                                       | `skipped`, con la nota `Skipped: why`                                                                                                                                                                               |
| Fallos de `expect.soft`           | El mensaje del primer fallo                                                                                                                      | Cada fallo del intento, en las notas                                                                                                                                                                                |
| Salida de consola                 | `--attach-output`: el `<system-out>` y el `<system-err>` de la prueba                                                                            | `captureOutput`: `stdout.log` y `stderr.log` de cada intento ([salida de consola](https://docs.probara.net/es/developers/ci-integrations/vitest/steps-and-attachments/#salida-de-consola))                                                  |
| Proyectos de Vitest               | Ningún proyecto: la salida JUnit de Vitest no lo nombra                                                                                          | Un parámetro `project` en cada resultado, cuando la configuración tiene más de uno ([proyectos de Vitest](https://docs.probara.net/es/developers/ci-integrations/vitest/sharding/#proyectos-de-vitest))                                     |
| Errores no gestionados (Vitest 5) | Se dejan fuera, con una advertencia por archivo; `--fail-on-failed-tests` sale con 3 por ellos                                                   | Se dejan fuera, con una advertencia; el código de salida sigue siendo el de Vitest ([errores fuera de una prueba](https://docs.probara.net/es/developers/ci-integrations/vitest/results-and-retries/#errores-fuera-de-una-prueba))          |
| Cuándo salen los resultados       | Después de toda la ejecución, en un paso posterior                                                                                               | Al final de la ejecución, desde el paso de pruebas; un [archivo de resultados](https://docs.probara.net/es/developers/ci-integrations/vitest/results-and-retries/#archivo-de-resultados) guarda lo que no se pudo enviar                    |

Lo mismo en los dos caminos, así que ningún caso se mueve:

- Los estados de las pruebas aprobadas, fallidas y omitidas, incluido `test.fails`, y las pruebas
  que `.only` o `-t` dejan fuera, que se omiten en los dos.
- Los resultados fallidos de un `beforeAll` o un `afterAll` que lanza un error, y de un archivo que
  no carga, con los nombres que les da el escritor JUnit de Vitest
  ([errores fuera de una prueba](https://docs.probara.net/es/developers/ci-integrations/vitest/results-and-retries/#errores-fuera-de-una-prueba)).
- Los IDs de caso en los títulos de las pruebas y de los `describe`, los nombres de las filas de
  `test.each` y `test.for`, y un `>` con un espacio a cada lado dentro de un título
  ([nombres tal como los escribe Vitest](https://docs.probara.net/es/developers/ci-integrations/vitest/linking/#nombres-tal-como-los-escribe-vitest)).

### Lo que la importación no puede saber

El XML contiene el nombre, el resultado, el tiempo y la salida de cada prueba. Lo que las pruebas le
cuentan al reporter, y lo que Vitest le da más allá de su salida JUnit, nunca llega a la
importación:

- **Cada intento** de una prueba reintentada o repetida, cada uno con su estado, su duración y sus
  errores.
- **Los IDs de caso de `probara.id()`**, que viven en los metadatos propios de la prueba, no en su
  título ([vincular](https://docs.probara.net/es/developers/ci-integrations/vitest/linking/)).
- **Los pasos**, anidados, con su estado y su duración, y los pasos de un caso nuevo
  ([pasos](https://docs.probara.net/es/developers/ci-integrations/vitest/steps-and-attachments/#probarastep)).
- **Los archivos**: `probara.attach()`, los archivos del propio `context.annotate()` de Vitest, las
  capturas de pantalla de los fallos en modo navegador
  ([adjuntos](https://docs.probara.net/es/developers/ci-integrations/vitest/steps-and-attachments/#probaraattach)).
- **Los parámetros, los comentarios, los enlaces y los campos de un caso nuevo**: los helpers
  `probara.*` ([metadatos y enlaces](https://docs.probara.net/es/developers/ci-integrations/vitest/metadata/)).
- **La selección de pruebas**: ejecutar solo las pruebas de una ejecución de Probara
  ([selección de pruebas](https://docs.probara.net/es/developers/ci-integrations/vitest/run-selection/)).

Conserva la importación donde el reporter no puede ejecutarse: un job que solo recibe el XML de otro
job, o un Vitest anterior a 3.2, que el reporter no admite (la importación se prueba con Vitest 3.2,
4.1 y 5.0). El lado de la importación está en la [CLI](https://docs.probara.net/es/developers/cli/) y en
[Vitest JUnit](https://github.com/cynch-me/probara-sdk/blob/main/packages/cli/docs/junit.md#vitest).

### Casos creados por una importación anterior

`probara import junit` lee la salida de Vitest con su dialecto `vitest`, que detecta por la raíz del
informe (`vitest tests`), sin ninguna opción. Las versiones de la CLI anteriores a ese dialecto
(0.3.0 y anteriores) leen el mismo XML como JUnit genérico, que da a cada prueba la misma clave, con
dos diferencias:

| Resultado                                                          | JUnit genérico (la CLI 0.3.0 y anteriores)            | El reporter, y el dialecto `vitest`                                 |
| ------------------------------------------------------------------ | ----------------------------------------------------- | ------------------------------------------------------------------- |
| Un fallo con el nombre de su archivo, como un archivo que no carga | Con la clave del archivo solo: `tests/broken.test.ts` | El archivo dos veces: `tests/broken.test.ts > tests/broken.test.ts` |
| Un error no gestionado (Vitest 5)                                  | Un resultado fallido propio                           | Se deja fuera, con una advertencia                                  |

Solo se mueven esos resultados: el siguiente fallo de un archivo así crea un caso con su clave
nueva, y el caso anterior no recibe más resultados. Todas las demás pruebas conservan su caso.

## Desde Qase

Pasar de `vitest-qase-reporter` es sobre todo cambiar nombres: la entrada del reporter, sus ajustes y
las llamadas `ctx.qase`, que se convierten en llamadas `probara.*` sin el envoltorio `withQase()`.
Esta sección relaciona cada una tal como la tiene el código de Qase (versión 1.5.1), que en algunos
puntos difiere de su documentación
([documentado, pero no en el código](#documentado-pero-no-en-el-código)), y después recoge lo que
funciona de otra manera y lo que no se porta, con los motivos.

1. `npm uninstall vitest-qase-reporter && npm i -D @probara/vitest-reporter`.
2. En `vitest.config.mts`, sustituye `['vitest-qase-reporter', {...}]` por
   `['@probara/vitest-reporter', {...}]`, traduciendo las opciones (abajo). Elimina
   `qase.config.json` y `.qaserc`: el reporter solo lee sus opciones y las variables `PROBARA_*`.
   Quita `vitest-qase-reporter/setup` de `setupFiles` si lo añadiste: el reporter añade su propio
   archivo de setup.
3. Sustituye en CI las variables `QASE_*` por variables `PROBARA_*`: como mínimo `PROBARA_API_TOKEN`
   (un token de aplicación de la tarjeta **Vitest**,
   [primeros pasos](https://docs.probara.net/es/developers/ci-integrations/vitest/#1-crea-un-token-de-aplicación)) y
   `PROBARA_PROJECT`.
4. Importa `probara` de `@probara/vitest-reporter`, vuelve a convertir cada
   `withQase(async ({ qase }) => {...})` en una función de prueba normal, y traduce las llamadas
   ([abajo](#las-llamadas-ctxqase)). Los IDs de caso pasan a ser IDs visibles de Probara
   (`SHOP-12`), no números.

Antes:

```ts
// vitest.config.mts
import { defineConfig } from 'vitest/config';

export default defineConfig({
  test: {
    reporters: [
      'default',
      [
        'vitest-qase-reporter',
        {
          mode: 'testops',
          testops: {
            project: 'SHOP',
            run: { title: 'Nightly', complete: true, tags: ['nightly'] },
            uploadAttachments: true,
          },
        },
      ],
    ],
  },
});
```

Después:

```ts
// vitest.config.mts
import { defineConfig } from 'vitest/config';

export default defineConfig({
  test: {
    reporters: [
      'default',
      [
        '@probara/vitest-reporter',
        { projectId: 'SHOP', run: { name: 'Nightly', tags: ['nightly'] }, captureOutput: true },
      ],
    ],
  },
});
```

`captureOutput` es opcional: adjunta la salida de consola de cada intento, algo que el `captureLogs`
de Qase nunca hizo con Vitest
([salida de consola](https://docs.probara.net/es/developers/ci-integrations/vitest/steps-and-attachments/#salida-de-consola)).
La ejecución se cierra al final sin `complete: true`, y `uploadAttachments` está activado por
defecto.

### Opciones y variables de Qase

| Opción de Qase (variable `QASE_*`)                                                    | Opción de Probara (variable `PROBARA_*`)                                                                                                                 |
| ------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `mode: 'testops'` (`QASE_MODE`)                                                       | Activado siempre que haya un token y un proyecto                                                                                                         |
| `mode: 'off'`                                                                         | `enabled: false` (`PROBARA_ENABLED=false`), o sin token                                                                                                  |
| `mode: 'report'`, `fallback` (`QASE_FALLBACK`)                                        | `resultsFile` (`PROBARA_RESULTS_FILE`) ([archivo de resultados](https://docs.probara.net/es/developers/ci-integrations/vitest/results-and-retries/#archivo-de-resultados))       |
| `report.connections.local.path`, `.format` (`QASE_REPORT_CONNECTION_PATH`, `_FORMAT`) | `resultsFile`, la ruta de un archivo JSON                                                                                                                |
| `mode: 'testops_multi'`, `testops_multi.*`                                            | `projectId` más `projects` y `run.ulids` ([varios proyectos](https://docs.probara.net/es/developers/ci-integrations/vitest/sharding/#varios-proyectos-de-probara))               |
| `debug` (`QASE_DEBUG`)                                                                | `debug` (`PROBARA_DEBUG`)                                                                                                                                |
| `environment` (`QASE_ENVIRONMENT`), un slug, que se ignora si no se encuentra         | `run.environment` (`PROBARA_ENVIRONMENT`), un nombre, que se crea si falta                                                                               |
| `rootSuite` (`QASE_ROOT_SUITE`)                                                       | `suiteUlid` (`PROBARA_SUITE_ULID`): una suite existente, por ULID                                                                                        |
| `captureLogs` (`QASE_CAPTURE_LOGS`): el reporter de Vitest de Qase lo ignora          | `captureOutput` (`PROBARA_CAPTURE_OUTPUT`)                                                                                                               |
| `statusMapping` (`QASE_STATUS_MAPPING`)                                               | `statusMapping` (`PROBARA_STATUS_MAPPING`), sin `invalid` ni `disabled`                                                                                  |
| `logging.console`, `logging.file` (`QASE_LOGGING_CONSOLE`, `QASE_LOGGING_FILE`)       | Los registros van a stderr; la opción `logger` para código                                                                                               |
| `testops.api.token` (`QASE_TESTOPS_API_TOKEN`)                                        | `PROBARA_API_TOKEN` (`apiToken` para código)                                                                                                             |
| `testops.api.host` (`QASE_TESTOPS_API_HOST`)                                          | `baseUrl` (`PROBARA_BASE_URL`), una URL completa                                                                                                         |
| `testops.api.timeout` (`QASE_TESTOPS_API_TIMEOUT`), en segundos                       | `timeoutMs`, en milisegundos, para cada petición                                                                                                         |
| `testops.api.retries`, `testops.api.retryBackoff`                                     | `maxRetries`, para cada petición; el backoff es fijo ([reintentos y backoff](https://docs.probara.net/es/developers/ci-integrations/vitest/configuration/#reintentos-y-backoff)) |
| `testops.project` (`QASE_TESTOPS_PROJECT`)                                            | `projectId` (`PROBARA_PROJECT`)                                                                                                                          |
| `testops.uploadAttachments` (`QASE_TESTOPS_UPLOAD_ATTACHMENTS`)                       | `uploadAttachments` (`PROBARA_UPLOAD_ATTACHMENTS`)                                                                                                       |
| `testops.attachments.concurrency`, `.timeout`                                         | `attachmentConcurrency`; las subidas tienen al menos 120 s ([tiempos máximos](https://docs.probara.net/es/developers/ci-integrations/vitest/configuration/#tiempos-máximos))     |
| `testops.run.id` (`QASE_TESTOPS_RUN_ID`)                                              | `run.ulid` (`PROBARA_RUN_ULID`)                                                                                                                          |
| `testops.run.title`, `.description`, `.tags`                                          | `run.name`, `run.description`, `run.tags` (`PROBARA_RUN_*`)                                                                                              |
| `testops.run.complete` (`QASE_TESTOPS_RUN_COMPLETE`)                                  | `closeRun` (`PROBARA_CLOSE_RUN`); una ejecución creada se cierra por defecto                                                                             |
| `testops.plan.id` (`QASE_TESTOPS_PLAN_ID`)                                            | `run.plan` (`PROBARA_PLAN`): el ID visible o el nombre del plan                                                                                          |
| `testops.configurations.values` (`group=value`)                                       | `run.configurations` (`PROBARA_CONFIGURATIONS=Group=Name`)                                                                                               |
| `testops.statusFilter` (`QASE_TESTOPS_STATUS_FILTER`)                                 | `statusFilter` (`PROBARA_STATUS_FILTER`)                                                                                                                 |
| `testops.batch.size` (`QASE_TESTOPS_BATCH_SIZE`)                                      | `chunkSize` (de 1 a 500)                                                                                                                                 |
| (ninguna)                                                                             | `run.milestone` (`PROBARA_MILESTONE`): el reporter de Qase no tiene hito                                                                                 |

Los ajustes de Qase sin equivalente están en [lo que no se porta](#lo-que-no-se-porta-de-qase).
Todas las opciones de Probara están en la página de
[configuración](https://docs.probara.net/es/developers/ci-integrations/vitest/configuration/#opciones).

### Las llamadas `ctx.qase`

| Qase                                                                  | Probara                                                                                                                                                                                             |
| --------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `test(addQaseId('pays', [12]), ...)`, `(Qase ID: 12)` en un título    | `test('SHOP-12 pays', ...)`: un ID en el título o en un `describe` ([vincular](https://docs.probara.net/es/developers/ci-integrations/vitest/linking/))                                                                     |
| `addQaseId('pays', [12, 13])`                                         | `'SHOP-12 SHOP-13 pays'`, o `probara.id(['SHOP-12', 'SHOP-13'])`                                                                                                                                    |
| `addQaseProjects('pays', { WEB: [3] })`, `(Qase WEB: 3)` en un título | `WEB-3` en el título, con `projects: ['WEB']` ([varios proyectos](https://docs.probara.net/es/developers/ci-integrations/vitest/sharding/#varios-proyectos-de-probara))                                                     |
| `withQase(async ({ qase }) => {...})`                                 | La propia función de prueba: `probara` encuentra por sí solo la prueba en curso                                                                                                                     |
| `await qase.title('...')`                                             | `probara.title('...')`                                                                                                                                                                              |
| `await qase.suite('Payments - Cards')`                                | `probara.suite(['Payments', 'Cards'])`                                                                                                                                                              |
| `await qase.fields({ severity: 'critical' })`                         | `probara.fields({ severity: 'critical' })`                                                                                                                                                          |
| `await qase.tags('smoke', 'checkout')`                                | `probara.tags('smoke', 'checkout')`                                                                                                                                                                 |
| `await qase.comment('...')`                                           | `probara.comment('...')`                                                                                                                                                                            |
| `await qase.parameters({...})`, `await qase.groupParameters({...})`   | `probara.parameters({...})`                                                                                                                                                                         |
| `await qase.step('Pay', fn, 'Paid', 'card=visa')`                     | `await probara.step('Pay', fn, { expected: 'Paid', data: 'card=visa' })`                                                                                                                            |
| `await qase.attach({ paths: [a, b] })`                                | `await probara.attach({ name, path })`, una vez por archivo                                                                                                                                         |
| `await qase.attach({ name, content, type })`                          | `await probara.attach({ name, body, contentType })`                                                                                                                                                 |
| `await qase.annotate(message, options)`                               | `await context.annotate(message, options)`: el del propio Vitest; su archivo se envía ([anotaciones de Vitest](https://docs.probara.net/es/developers/ci-integrations/vitest/steps-and-attachments/#anotaciones-de-vitest)) |
| (ninguna: el reporter de Vitest de Qase no tiene `ignore`)            | `probara.ignore()`                                                                                                                                                                                  |
| (ninguna)                                                             | `probara.link()` y `probara.issue()` ([enlaces e incidencias](https://docs.probara.net/es/developers/ci-integrations/vitest/metadata/#enlaces-e-incidencias))                                                               |

Quita los marcadores `(Qase ID: 12)` de los títulos: el reporter los lee como texto, así que se
quedarían en la clave de automatización.

Antes:

```ts
// tests/checkout.test.ts
import { test } from 'vitest';
import { addQaseId, withQase } from 'vitest-qase-reporter/vitest';

test(
  addQaseId('pays with a card', [12]),
  withQase(async ({ qase }) => {
    await qase.fields({ severity: 'critical' });
    await qase.tags('smoke');
    await qase.step('Pay', async () => {}, 'The order is paid');
    await qase.attach({ name: 'order.json', content: '{"id":1042}', type: 'application/json' });
  }),
);
```

Después:

```ts
// tests/checkout.test.ts
import { probara } from '@probara/vitest-reporter';
import { test } from 'vitest';

test('SHOP-12 pays with a card', async () => {
  probara.fields({ severity: 'critical' }).tags('smoke');
  await probara.step('Pay', async () => {}, { expected: 'The order is paid' });
  await probara.attach({
    name: 'order.json',
    body: '{"id":1042}',
    contentType: 'application/json',
  });
});
```

```json
[
  {
    "caseDisplayId": "SHOP-12",
    "automationKey": "tests/checkout.test.ts > pays with a card",
    "status": "passed",
    "steps": [{ "action": "Pay", "expected": "The order is paid", "status": "passed" }],
    "case": {
      "tags": ["smoke"],
      "fields": { "severity": "critical" },
      "steps": [{ "action": "Pay", "expected": "The order is paid" }]
    }
  }
]
```

El resultado lleva además `order.json` (`application/json`). El ID sale del título y de la clave:
añadirlo o quitarlo nunca cambia el caso con el que coincide la prueba
([claves de automatización](https://docs.probara.net/es/developers/ci-integrations/vitest/linking/#claves-de-automatización)).

### Documentado, pero no en el código

El README y las guías de Qase muestran llamadas y ajustes que la versión 1.5.1 no tiene, o que tiene
con otro comportamiento. El código copiado de ahí falla o hace otra cosa:

| Documentación de Qase                                                                                            | Código de Qase (1.5.1)                                                        | Con Probara                                                                                                                                    |
| ---------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| `test(qase(12, 'pays'), ...)`, con `qase` importado de `vitest-qase-reporter` o de `vitest-qase-reporter/vitest` | No se exporta ningún `qase`: solo `withQase`, `addQaseId` y `addQaseProjects` | `test('SHOP-12 pays', ...)`                                                                                                                    |
| `qase.title()`, `qase.fields()`, `qase.suite()` sobre esa importación, en cualquier prueba                       | Solo `ctx.qase`, en una función que envuelve `withQase()`                     | `probara.title()`, `probara.fields()`, `probara.suite()`, en cualquier prueba                                                                  |
| `qase.ignore()`                                                                                                  | No está en el código                                                          | `probara.ignore()`                                                                                                                             |
| `qase.mute()`                                                                                                    | No está en el código                                                          | No se porta ([abajo](#lo-que-no-se-porta-de-qase))                                                                                             |
| `setupFiles: ['vitest-qase-reporter/setup']`, para el perfilador de red                                          | El paquete no lo exporta: Vitest no puede cargarlo                            | Nada que añadir: el reporter añade su propio archivo de setup                                                                                  |
| `qase.suite('Payments\tCards')`, un tabulador entre suites                                                       | Solo divide por un guion con un espacio a cada lado (`'Payments - Cards'`)    | `probara.suite(['Payments', 'Cards'])`                                                                                                         |
| Pasos anidados en pasos                                                                                          | Un paso que contiene pasos se pierde; solo se conservan los internos          | Anidados tal como se ejecutaron ([pasos](https://docs.probara.net/es/developers/ci-integrations/vitest/steps-and-attachments/#probarastep))                            |
| Un adjunto de un paso                                                                                            | Siempre es de la prueba                                                       | `probara.attach()` dentro de un paso ([archivos de un paso](https://docs.probara.net/es/developers/ci-integrations/vitest/steps-and-attachments/#archivos-de-un-paso)) |
| Las opciones del reporter ganan a las variables `QASE_*`, que ganan a `qase.config.json`                         | Las variables ganan al archivo, que gana a las opciones                       | Las opciones y después las variables ([precedencia](https://docs.probara.net/es/developers/ci-integrations/vitest/configuration/#fuentes-y-precedencia))               |
| `testops.run.complete` vale `true` por defecto                                                                   | Sin definir, deja la ejecución abierta                                        | Una ejecución creada se cierra por defecto ([cierre](https://docs.probara.net/es/developers/ci-integrations/vitest/results-and-retries/#cierre))                       |
| `report.connection.*`                                                                                            | `report.connections.*`                                                        | `resultsFile`                                                                                                                                  |
| El modo watch reporta al salir o al volver a ejecutar todas las pruebas                                          | Cada nueva ejecución crea una ejecución nueva, salvo que se defina `run.id`   | Una sola ejecución para toda la sesión ([modo watch](https://docs.probara.net/es/developers/ci-integrations/vitest/configuration/#modo-watch))                         |
| `api.token` por proyecto de `testops_multi`                                                                      | Un solo token para todos los proyectos                                        | Un solo token de aplicación para todos los proyectos de la organización                                                                        |

### Qué es distinto respecto a Qase, y por qué

| Tema                                   | Qase                                                                                                                          | Probara                                                                                                                                                                                                                                       |
| -------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Precedencia                            | Las variables ganan a `qase.config.json`, que gana a las opciones del reporter                                                | Las opciones de la configuración de Vitest, después las variables `PROBARA_*`, y después los valores por defecto: lo que dice la configuración es lo que se ejecuta                                                                           |
| Archivo de configuración               | `qase.config.json` o `.qaserc`                                                                                                | Ninguno: un archivo de configuración con un token podría subirse al repositorio. Las opciones viven en la configuración de Vitest                                                                                                             |
| IDs de caso                            | Números, solo en el título de la propia prueba                                                                                | IDs visibles (`SHOP-12`) en el título o en cualquier `describe`: el ID del Proyecto dirige cada ID a su proyecto                                                                                                                              |
| Los helpers                            | `ctx.qase`, dentro de `withQase()`, hasta que termina el cuerpo de la prueba: Vitest rechaza sus anotaciones en `afterEach`   | `probara`, importado una vez, funciona en el cuerpo, en `beforeEach`, en `afterEach`, en `onTestFinished` y en las funciones a las que llaman ([dónde llamarlos](https://docs.probara.net/es/developers/ci-integrations/vitest/metadata/#dónde-llamar-a-los-helpers)) |
| Pasos                                  | Planos; el resultado esperado y los datos se codifican en el nombre del paso; sin duración                                    | Anidados tal como se ejecutaron, cada uno con su estado, su duración y su error; los más externos pasan a ser los pasos de un caso nuevo ([pasos](https://docs.probara.net/es/developers/ci-integrations/vitest/steps-and-attachments/#probarastep))                  |
| Estados                                | `invalid` para un fallo cuyo error no parece una aserción (una suposición a partir del mensaje)                               | `passed`, `failed`, `skipped`; `blocked` solo mediante `statusMapping`. Sin suposiciones a partir del texto del error ([estados](https://docs.probara.net/es/developers/ci-integrations/vitest/results-and-retries/#estados))                                         |
| Errores                                | El mensaje del primer error, que `comment()` sustituye; cada traza en un campo propio                                         | Cada error del intento en las notas, incluidos los fallos de `expect.soft`; un comentario va primero y no oculta ninguno                                                                                                                      |
| Llamadas a `fields()` y `parameters()` | La última llamada sustituye a las demás                                                                                       | Se combinan por nombre                                                                                                                                                                                                                        |
| Campos del caso                        | Se envían con cada resultado                                                                                                  | Solo cuando el informe crea el caso: un caso existente conserva lo que escribieron las personas ([solo casos creados](https://docs.probara.net/es/developers/ci-integrations/vitest/metadata/#solo-casos-creados))                                                    |
| Reintentos                             | Solo el intento final                                                                                                         | Cada intento es un resultado, sin marca de inestable: el historial del caso muestra la inestabilidad ([reintentos](https://docs.probara.net/es/developers/ci-integrations/vitest/results-and-retries/#reintentos-y-repeticiones))                                     |
| Cierre de la ejecución                 | Solo con `run.complete: true`; sin definir, queda abierta                                                                     | Una ejecución que crea el reporter se cierra al final, cuando se envió todo; una reutilizada queda abierta ([cierre](https://docs.probara.net/es/developers/ci-integrations/vitest/results-and-retries/#cierre))                                                      |
| Shards                                 | Cada shard crea su propia ejecución salvo que reciba un ID de ejecución; cerrar una ejecución compartida queda para otro paso | Una sola ejecución: `vitest --merge-reports` sobre los blobs de los shards, o `probara run create` con `PROBARA_RUN_ULID` ([sharding](https://docs.probara.net/es/developers/ci-integrations/vitest/sharding/))                                                       |
| Cuando falla el envío                  | `fallback` a un informe local                                                                                                 | Un archivo de resultados, que `probara import results` envía más tarde a las mismas ejecuciones ([archivo de resultados](https://docs.probara.net/es/developers/ci-integrations/vitest/results-and-retries/#archivo-de-resultados))                                   |
| Cadenas en `attach()`                  | Una cadena que parece base64 se decodifica (`'Hello'` sube basura)                                                            | Una cadena se envía tal cual; pasa un `Buffer` o un `Uint8Array` para contenido binario ([`probara.attach()`](https://docs.probara.net/es/developers/ci-integrations/vitest/steps-and-attachments/#probaraattach))                                                    |
| `attach({ paths })`                    | Una lista de rutas; el tipo de contenido es `type`, no `contentType`                                                          | Un archivo por llamada, `path` o `body`, y `contentType`                                                                                                                                                                                      |

### Lo que no se porta de Qase

| Función de Qase                                                               | Por qué                                                                                                                                                                                                                                                                                                           |
| ----------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Enlace público al informe (`showPublicReportLink`)                            | Un enlace público a los resultados de las pruebas es una decisión de compartir que corresponde a la aplicación Probara, no algo que deba activar un job de CI                                                                                                                                                     |
| Defectos automáticos (`testops.defect`)                                       | Registrar un defecto por cada fallo inunda la lista de defectos; reporta los defectos desde el resultado en Probara                                                                                                                                                                                               |
| Perfilador de red (`profilers: ['network']`, `networkProfiler`)               | Sus pasos no aportan nada, hasta los 200 pasos de un resultado, y no se pueden distinguir cuando las pruebas se ejecutan en paralelo; adjunta lo importante con `probara.attach()` ([sin perfilador de red](https://docs.probara.net/es/developers/ci-integrations/vitest/configuration/#no-disponible-un-perfilador-de-red))             |
| Enlace de Jira en las ejecuciones (`run.externalLink`)                        | Probara vincula las incidencias de Jira a los casos y a los defectos, donde vive la trazabilidad; `probara.issue()` vincula un resultado a una incidencia                                                                                                                                                         |
| `qase.mute()`                                                                 | Qase lo documenta, pero no está en su código; la clasificación vive en Probara, en los resultados                                                                                                                                                                                                                 |
| Crear configuraciones (`createIfNotExists`)                                   | Las configuraciones forman parte de la planificación de un proyecto; una desconocida se rechaza, así que una errata nunca crea una nueva                                                                                                                                                                          |
| El estado `invalid`                                                           | Es una suposición a partir del texto del error; Probara mantiene cuatro estados y el error completo en las notas                                                                                                                                                                                                  |
| Registrar en un archivo (`logging.file`)                                      | Los registros van a stderr, donde los guarda el CI; redirige stderr para conservar un archivo                                                                                                                                                                                                                     |
| Plan y entorno por proyecto (`testops_multi.projects[].plan`, `.environment`) | Las entidades de planificación pertenecen a un proyecto, donde un nombre desconocido rechazaría el informe: crea primero la ejecución de ese proyecto con `probara run create` y pásala en `run.ulids` ([una ejecución por proyecto](https://docs.probara.net/es/developers/ci-integrations/vitest/sharding/#una-ejecución-por-proyecto)) |
| Un archivo de configuración (`qase.config.json`, `.qaserc`)                   | Un archivo con un token podría subirse al repositorio: el token se queda en el entorno, y las opciones en la configuración de Vitest                                                                                                                                                                              |

## Desde Allure

`allure-vitest` es un reporter que añade su archivo de setup y su runner a cada proyecto de Vitest, y
escribe archivos de resultados en `allure-results/`; un paso aparte los convierte en un informe. El
reporter envía en cambio los resultados a Probara, y añade su propio archivo de setup y su runner del
mismo modo: cambia las entradas.

```ts
// vitest.config.mts
import { defineConfig } from 'vitest/config';

export default defineConfig({
  test: {
    reporters: [
      'default',
      [
        'allure-vitest/reporter',
        {
          resultsDir: 'allure-results',
          links: { issue: { urlTemplate: 'https://jira.example.com/browse/%s' } },
        },
      ],
    ],
  },
});
```

### Ajustes de Allure

| Allure                                      | Probara                                                                                                                                                                          |
| ------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `resultsDir`                                | Nada: los resultados van a Probara. Un [archivo de resultados](https://docs.probara.net/es/developers/ci-integrations/vitest/results-and-retries/#archivo-de-resultados) guarda lo que no se pudo enviar |
| `links.issue.urlTemplate`                   | `issueUrlTemplate` (`PROBARA_ISSUE_URL_TEMPLATE`) ([`probara.issue()`](https://docs.probara.net/es/developers/ci-integrations/vitest/metadata/#probaraissue-e-issueurltemplate))                         |
| `environmentInfo`                           | `run.environment` (un nombre) y el origen de la ejecución, detectado del CI ([la ejecución](https://docs.probara.net/es/developers/ci-integrations/vitest/results-and-retries/#la-ejecución))            |
| `globalLabels`, `ALLURE_LABEL_*`            | `run.tags` (`PROBARA_RUN_TAGS`) para la ejecución; las etiquetas de un caso nuevo salen de `probara.tags()`                                                                      |
| `ALLURE_TESTPLAN_PATH`                      | `runCasesOnly` con `PROBARA_RUN_ULID` ([selección de pruebas](https://docs.probara.net/es/developers/ci-integrations/vitest/run-selection/))                                                             |
| `reportMatchers`, `categories`, `listeners` | No se portan ([lo que el reporter no admite](#lo-que-el-reporter-no-admite))                                                                                                     |

### Llamadas de Allure

Las llamadas de Allure son asíncronas (`await allure.tags('smoke')`); las de Probara vuelven en el
acto, salvo `probara.attach()` y un `probara.step()` con un cuerpo asíncrono, que esperas con
`await`.

| Allure (`allure-js-commons`)                     | Probara (`probara.*`)                                                                   |
| ------------------------------------------------ | --------------------------------------------------------------------------------------- |
| `@allure.id:12` en un título, `allureId('12')`   | `SHOP-12` en el título o en un `describe`, o `id('SHOP-12')`                            |
| `displayName('...')`                             | `title('...')`                                                                          |
| `description('...')`                             | `fields({ description: '...' })`                                                        |
| `severity('critical')`, `layer('api')`           | `fields({ severity: 'critical', layer: 'api' })`                                        |
| `tag('smoke')`, `tags(...)`                      | `tags('smoke')`                                                                         |
| `epic`, `feature`, `story`, `label(name, value)` | `fields({ Feature: 'Checkout' })`: un campo personalizado con ese nombre, o `tags(...)` |
| `parentSuite`, `suite`, `subSuite`               | `suite(['Payments', 'Cards'])`                                                          |
| `parameter('user', 'ana')`                       | `parameters({ user: 'ana' })`                                                           |
| `step('Pay', async () => {...})`                 | `step('Pay', async () => {...})`                                                        |
| `logStep('Paid')`                                | `step('Paid')`: un paso sin cuerpo, aprobado                                            |
| `attachment(name, content, { contentType })`     | `attach({ name, body: content, contentType })`                                          |
| `attachmentPath(name, path)`                     | `attach({ name, path })`                                                                |
| `link(url, name)`                                | `link(url, name)`                                                                       |
| `issue('PAY-7')`                                 | `issue('PAY-7')`, con `issueUrlTemplate`                                                |
| `tms('12')`                                      | `id('SHOP-12')`                                                                         |

```ts
reporters: [
  'default',
  ['@probara/vitest-reporter', { issueUrlTemplate: 'https://jira.example.com/browse/%s' }],
],
```

```ts
// tests/payments.test.ts
import { probara } from '@probara/vitest-reporter';
import { test } from 'vitest';

test('refunds an order', async () => {
  probara.fields({ severity: 'critical' }).issue('PAY-7');
  await probara.step('Open the order', async () => {});
  await probara.step('Refund it', async () => {});
});
```

```json
[
  {
    "automationKey": "tests/payments.test.ts > refunds an order",
    "steps": [
      { "action": "Open the order", "status": "passed" },
      { "action": "Refund it", "status": "passed" }
    ],
    "links": [{ "url": "https://jira.example.com/browse/PAY-7", "name": "PAY-7" }],
    "case": {
      "fields": { "severity": "critical" },
      "steps": [{ "action": "Open the order" }, { "action": "Refund it" }]
    }
  }
]
```

### Qué funciona distinto respecto a Allure

| Tema                                      | Allure                                                                                  | Probara                                                                                                                                                                                                                                                                                                       |
| ----------------------------------------- | --------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Estados                                   | `failed` para una aserción, `broken` para cualquier otro error                          | `failed` para ambos, con el error en las notas: sin suposiciones a partir del tipo de error ([estados](https://docs.probara.net/es/developers/ci-integrations/vitest/results-and-retries/#estados))                                                                                                                                   |
| `test.skip`, un `describe` omitido        | No se reportan                                                                          | Resultados omitidos, como los tiene la salida JUnit de Vitest                                                                                                                                                                                                                                                 |
| Aserciones                                | Cada `expect` es un paso (`reportMatchers`, activado por defecto)                       | Los pasos son las llamadas a `probara.step()`                                                                                                                                                                                                                                                                 |
| Una llamada donde no corre ninguna prueba | Una llamada en `beforeAll` o en el cuerpo de un `describe` va a cada prueba de la suite | Se deja fuera, con una advertencia: ahí no corre una sola prueba ([dónde llamar a los helpers](https://docs.probara.net/es/developers/ci-integrations/vitest/metadata/#dónde-llamar-a-los-helpers))                                                                                                                                   |
| Un hook que lanza un error                | Su error es un error global del informe, junto a las pruebas                            | El veredicto de Vitest: un `beforeEach` o `afterEach` que falla hace fallar su prueba, un `beforeAll` o `afterAll` que falla es un resultado fallido de su `describe` o de su archivo ([errores fuera de una prueba](https://docs.probara.net/es/developers/ci-integrations/vitest/results-and-retries/#errores-fuera-de-una-prueba)) |
| Reintentos                                | Un resultado por prueba, el intento final                                               | Cada intento es un resultado de la ejecución ([reintentos](https://docs.probara.net/es/developers/ci-integrations/vitest/results-and-retries/#reintentos-y-repeticiones))                                                                                                                                                             |
| Modo watch                                | Archivos de resultados nuevos en cada nueva ejecución, que se acumulan en `resultsDir`  | Una sola ejecución para toda la sesión ([modo watch](https://docs.probara.net/es/developers/ci-integrations/vitest/configuration/#modo-watch))                                                                                                                                                                                        |
| Campos del caso                           | Etiquetas en cada resultado                                                             | Campos y etiquetas del caso que crea un informe; un caso existente conserva los suyos ([solo casos creados](https://docs.probara.net/es/developers/ci-integrations/vitest/metadata/#solo-casos-creados))                                                                                                                              |
| `test.concurrent` en modo navegador       | Una llamada puede ir a la prueba equivocada después de un `await` (lo dice su README)   | Una llamada se descarta con una advertencia, nunca se registra en otra prueba ([modo navegador](https://docs.probara.net/es/developers/ci-integrations/vitest/browser-mode/#aún-no-disponible))                                                                                                                                       |

## Desde ReportPortal

`@reportportal/agent-js-vitest` es un objeto reporter en `reporters`, con un archivo de setup que
listas en `setupFiles` para su `ReportingApi`. Sustituye ambos por la entrada del reporter de
arriba:

```ts
// vitest.config.mts
import RPReporter from '@reportportal/agent-js-vitest';
import { defineConfig } from 'vitest/config';

export default defineConfig({
  test: {
    setupFiles: ['@reportportal/agent-js-vitest/setup'],
    reporters: [
      'default',
      new RPReporter({
        apiKey: process.env.RP_API_KEY,
        endpoint: 'https://rp.example.com/api/v2',
        project: 'shop',
        launch: 'Nightly',
      }),
    ],
  },
});
```

### Ajustes de ReportPortal

| ReportPortal (variable)                             | Probara (variable)                                                                                                                                                                |
| --------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `apiKey`, `oauth`                                   | `PROBARA_API_TOKEN`: un token de aplicación de la tarjeta **Vitest** ([primeros pasos](https://docs.probara.net/es/developers/ci-integrations/vitest/#1-crea-un-token-de-aplicación))                     |
| `endpoint`                                          | `baseUrl` (`PROBARA_BASE_URL`), la URL de la aplicación sin ruta                                                                                                                  |
| `project`                                           | `projectId` (`PROBARA_PROJECT`)                                                                                                                                                   |
| `launch`                                            | `run.name` (`PROBARA_RUN_NAME`)                                                                                                                                                   |
| `description`                                       | `run.description` (`PROBARA_RUN_DESCRIPTION`)                                                                                                                                     |
| `attributes` (`[{ key: 'build', value: '42' }]`)    | `run.tags` (`PROBARA_RUN_TAGS=build:42,nightly`): las etiquetas son nombres simples, con dos puntos incluidos                                                                     |
| `launchId` (`RP_LAUNCH_ID`)                         | `run.ulid` (`PROBARA_RUN_ULID`)                                                                                                                                                   |
| `launchUuidPrint`                                   | `probara run create` imprime el ULID en el que reporta cada shard ([una ejecución existente](https://docs.probara.net/es/developers/ci-integrations/vitest/results-and-retries/#una-ejecución-existente)) |
| `restClientConfig` (timeout, retries, proxy)        | `timeoutMs`, `maxRetries`; un proxy mediante las variables de Node ([red](https://docs.probara.net/es/developers/ci-integrations/vitest/configuration/#red))                                              |
| `headers`                                           | Un `fetch` propio ([tu propio cliente HTTP](https://docs.probara.net/es/developers/ci-integrations/vitest/configuration/#tu-propio-cliente-http))                                                         |
| `debug`                                             | `debug` (`PROBARA_DEBUG`)                                                                                                                                                         |
| `extendTestDescriptionWithLastError`                | Nada que definir: cada error de un intento está en sus notas                                                                                                                      |
| `skippedIssue`, `rerun`, `rerunOf`, `mode: 'DEBUG'` | No se portan ([lo que el reporter no admite](#lo-que-el-reporter-no-admite))                                                                                                      |

### Llamadas de ReportPortal

| ReportPortal (`ReportingApi`)                            | Probara (`probara.*`)                                                                                                                                              |
| -------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `ReportingApi.attachment(task, { name, type, content })` | `attach({ name, body, contentType })`, con un `Buffer` o texto, o `attach({ name, path })`                                                                         |
| `ReportingApi.description(task, text)`                   | `fields({ description: text })`, para un caso nuevo                                                                                                                |
| `ReportingApi.attributes(task, [{ key, value }])`        | `parameters({ [key]: value })` en el resultado, o `tags(value)` en un caso nuevo                                                                                   |
| `ReportingApi.testCaseId(task, id)`                      | No se porta: vincula un caso por su ID ([vincular](https://docs.probara.net/es/developers/ci-integrations/vitest/linking/))                                                                |
| `ReportingApi.log(task, message, level)`                 | No se porta: `comment()` para una línea en las notas, `captureOutput` para la consola                                                                              |
| `console.log()`, `console.error()`, como registros       | `captureOutput`: `stdout.log` y `stderr.log` de cada intento ([salida de consola](https://docs.probara.net/es/developers/ci-integrations/vitest/steps-and-attachments/#salida-de-consola)) |
| El `ReportingApi` global del archivo de setup            | `probara`, importado: no hay ningún `task` que pasar                                                                                                               |

Una cadena en base64 como `content` se enviaría como texto: pasa a `attach()` la `path` del archivo,
o sus bytes como un `Buffer`.

```ts
// tests/checkout.test.ts
import { probara } from '@probara/vitest-reporter';
import { test } from 'vitest';

test('pays with a card', async () => {
  probara.fields({ description: 'Pays with a saved card' }).parameters({ browser: 'chromium' });
  await probara.attach({
    name: 'response',
    body: '{"status":"paid"}',
    contentType: 'application/json',
  });
});
```

```json
[
  {
    "automationKey": "tests/checkout.test.ts > pays with a card",
    "parameters": { "browser": "chromium" },
    "case": { "description": "Pays with a saved card" }
  }
]
```

El resultado lleva además `response.json` (`application/json`).

### Qué funciona distinto respecto a ReportPortal

| Tema                    | ReportPortal                                                                                             | Probara                                                                                                                                        |
| ----------------------- | -------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| API dentro de la prueba | `ReportingApi`, global desde su archivo de setup, o pasando el `task` de la prueba                       | `probara`, importado; cada llamada pertenece a la prueba que la hizo ([metadatos](https://docs.probara.net/es/developers/ci-integrations/vitest/metadata/))            |
| `test.concurrent`       | El `ReportingApi` global sigue a la última prueba que empezó, así que una llamada puede ir a otra prueba | Cada llamada va a su propia prueba                                                                                                             |
| Reintentos              | Solo el intento final                                                                                    | Cada intento es un resultado ([reintentos](https://docs.probara.net/es/developers/ci-integrations/vitest/results-and-retries/#reintentos-y-repeticiones))              |
| `test.todo`             | Omitida, con el atributo `todo`                                                                          | Omitida, con la nota `Todo`                                                                                                                    |
| Un `describe` omitido   | Un elemento de suite omitido; sus pruebas no se reportan                                                 | Cada prueba es un resultado omitido                                                                                                            |
| Errores                 | El primer error, como registro y en la descripción                                                       | Cada error del intento, en las notas de su resultado                                                                                           |
| Estructura              | Un elemento de suite por cada `describe`                                                                 | Cada caso nuevo en una suite con el nombre de su archivo ([casos que faltan](https://docs.probara.net/es/developers/ci-integrations/vitest/linking/#casos-que-faltan)) |
| Modo watch              | El launch termina con la primera ejecución, y las siguientes no tienen ninguno                           | Una sola ejecución para toda la sesión ([modo watch](https://docs.probara.net/es/developers/ci-integrations/vitest/configuration/#modo-watch))                         |

## Desde TestRail

TestRail no tiene reporter para Vitest: un pipeline escribe la salida JUnit de Vitest, y después la
CLI de Python de TestRail, `trcli parse_junit`, la envía tras la ejecución. El reporter sustituye
ambos pasos, y envía lo que el XML no puede contener: pasos, archivos, comentarios y cada intento.

```bash
npx vitest run --reporter=junit --outputFile=reports/vitest.xml
trcli -h https://example.testrail.io --project "Shop" -u ci@example.com -k "$TESTRAIL_KEY" \
  parse_junit --title "Nightly" --case-matcher name -f reports/vitest.xml --close-run
```

Con el reporter, basta con el paso de pruebas:

```bash
npx vitest run
```

### Ajustes de trcli

| trcli                                         | Probara                                                                                                                                                               |
| --------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `-h` (host), `-u`, `-p`, `-k`                 | `baseUrl` para un Probara autoalojado; `PROBARA_API_TOKEN`                                                                                                            |
| `--project` (un nombre)                       | `projectId` (`PROBARA_PROJECT`), el ID del Proyecto                                                                                                                   |
| `--title`, `--run-description`                | `run.name`, `run.description`                                                                                                                                         |
| `--run-id`                                    | `run.ulid` (`PROBARA_RUN_ULID`)                                                                                                                                       |
| `--plan-id`, `--config-ids`, `--milestone-id` | `run.plan`, `run.configurations`, `run.milestone`, por ID visible o nombre ([la ejecución](https://docs.probara.net/es/developers/ci-integrations/vitest/results-and-retries/#la-ejecución))  |
| `--close-run`                                 | El comportamiento por defecto para una ejecución que crea el reporter; `closeRun` para una reutilizada                                                                |
| `--case-matcher name` (`C123` en el nombre)   | `SHOP-12` en el título o en un `describe` ([vincular](https://docs.probara.net/es/developers/ci-integrations/vitest/linking/))                                                                |
| `--case-matcher auto` (`classname.name`)      | La clave de automatización: el archivo, los describes y el título                                                                                                     |
| `-y` (crear secciones y casos)                | `createMissingCases`, activado por defecto; las suites salen del archivo de prueba                                                                                    |
| `--suite-id`, `--section-id`                  | `suiteUlid` (`PROBARA_SUITE_ULID`): la suite bajo la que van los casos creados                                                                                        |
| `--case-fields type_id:3`                     | `probara.fields({ type: 'Regression' })` en la prueba, por nombre ([metadatos](https://docs.probara.net/es/developers/ci-integrations/vitest/metadata/))                                      |
| `-a`, `--assign` (emails)                     | `assignFailedTo` (`PROBARA_ASSIGN_FAILED_TO`) ([asignar resultados fallidos](https://docs.probara.net/es/developers/ci-integrations/vitest/results-and-retries/#asignar-resultados-fallidos)) |
| `-b`, `--batch-size`                          | `chunkSize` (de 1 a 500)                                                                                                                                              |
| `-t`, `--timeout` (segundos)                  | `timeoutMs` (milisegundos, para cada intento de una petición)                                                                                                         |
| `--proxy`, `--noproxy`                        | `HTTPS_PROXY` y `NO_PROXY` con `NODE_USE_ENV_PROXY=1` ([proxies](https://docs.probara.net/es/developers/ci-integrations/vitest/configuration/#proxies))                                       |
| `-v` (verbose)                                | `debug` (`PROBARA_DEBUG`)                                                                                                                                             |
| Propiedad `testrail_result_comment`           | `probara.comment()`                                                                                                                                                   |
| Propiedad `testrail_attachment`               | `probara.attach()`                                                                                                                                                    |
| Propiedad `testrail_result_step`              | `probara.step()`                                                                                                                                                      |

### Qué funciona distinto respecto a trcli

| Tema                      | trcli sobre la salida JUnit de Vitest                                          | Probara                                                                                                                                                                                |
| ------------------------- | ------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Pruebas omitidas          | Retest                                                                         | Omitidas                                                                                                                                                                               |
| `test.todo`               | Retest: Vitest la escribe como omitida                                         | Omitida, con la nota `Todo`                                                                                                                                                            |
| Reintentos                | Un resultado por prueba                                                        | Cada intento ([reintentos](https://docs.probara.net/es/developers/ci-integrations/vitest/results-and-retries/#reintentos-y-repeticiones))                                                                      |
| Describes anidados        | Una sección por archivo de prueba, plana: Vitest escribe una suite por archivo | La suite de un caso creado es su archivo; `probara.suite()` nombra suites anidadas                                                                                                     |
| Duraciones                | Redondeadas a segundos (`--allow-ms` para milisegundos)                        | Milisegundos, y el inicio de cada intento                                                                                                                                              |
| Un ID de caso desconocido | Aborta la subida                                                               | Ese resultado no se registra, y el registro lo nombra; los demás sí                                                                                                                    |
| Una subida fallida        | Deshace la ejecución, los casos y las secciones que creó                       | No se deshace nada; un [archivo de resultados](https://docs.probara.net/es/developers/ci-integrations/vitest/results-and-retries/#archivo-de-resultados) guarda lo que no se envió                             |
| Shards                    | Una subida del archivo de cada shard, con un patrón                            | `vitest --merge-reports` sobre los blobs, o `probara run create`, `PROBARA_RUN_ULID` en cada shard y `probara run close` ([sharding](https://docs.probara.net/es/developers/ci-integrations/vitest/sharding/)) |

## Lo que el reporter no admite

Cada una de estas funciones se deja fuera, con su motivo:

| Función                                                                   | En                                                                                                    | Por qué                                                                                                                                                                                                                                                                                                                                                                   |
| ------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Actualizar casos existentes                                               | trcli (`--update-existing-cases`, `--update-strategy`)                                                | Un informe nunca cambia un caso existente: lo que hay escrito ahí es de las personas ([solo casos creados](https://docs.probara.net/es/developers/ci-integrations/vitest/metadata/#solo-casos-creados))                                                                                                                                                                                           |
| Una marca de inestable                                                    | Vitest (`diagnostic().flaky`)                                                                         | Cada intento es un resultado: un caso cuyo historial mezcla fallos y aprobados en una ejecución es inestable, sin necesidad de marca ([reintentos](https://docs.probara.net/es/developers/ci-integrations/vitest/results-and-retries/#reintentos-y-repeticiones))                                                                                                                                 |
| Aserciones como pasos                                                     | Allure (`reportMatchers`)                                                                             | Cada `expect` sería un paso, hasta los 200 pasos de un resultado, y desplazaría a los pasos que dicen lo que hace la prueba: envuelve lo importante en `probara.step()`                                                                                                                                                                                                   |
| Los estados `broken` e `invalid`                                          | Allure (`broken`), Qase (`invalid`)                                                                   | Los dos son una suposición a partir del tipo o del texto del error; Probara mantiene cuatro estados (`passed`, `failed`, `skipped`, `blocked`) y el error completo en las notas                                                                                                                                                                                           |
| Parámetros enmascarados, ocultos y excluidos                              | Allure (los modos de `parameter()`)                                                                   | Los parámetros se muestran con el resultado y nunca forman parte de la clave: deja fuera un secreto en lugar de enmascararlo                                                                                                                                                                                                                                              |
| Niveles de registro y una API de registro                                 | ReportPortal (`ReportingApi.log()`), Allure (`logStep()` con un estado)                               | Probara no tiene una entidad de registro: `captureOutput` guarda la salida de consola de cada intento, `probara.comment()` escribe una línea en las notas, y el estado de un paso es lo que hizo su cuerpo                                                                                                                                                                |
| Defectos, enlaces de Jira en las ejecuciones, enlaces públicos a informes | Qase (`testops.defect`, `run.externalLink`, `showPublicReportLink`)                                   | Los defectos y su clasificación viven en Probara, en los resultados; `probara.issue()` vincula un resultado a una incidencia; un enlace público a los resultados es una decisión de compartir, no un ajuste de CI                                                                                                                                                         |
| Un perfilador de red                                                      | Qase (`profilers: ['network']`)                                                                       | Sus pasos no aportan nada, hasta los 200 pasos de un resultado, y no se pueden distinguir cuando las pruebas se ejecutan en paralelo ([sin perfilador de red](https://docs.probara.net/es/developers/ci-integrations/vitest/configuration/#no-disponible-un-perfilador-de-red))                                                                                                                   |
| Categorías, problemas conocidos, pruebas silenciadas                      | Allure (`categories`), ReportPortal (`skippedIssue`), Qase (`qase.mute()`, solo documentado)          | La clasificación vive en Probara, en los resultados y los defectos, no en un ajuste de CI                                                                                                                                                                                                                                                                                 |
| Sustituir la clave de automatización                                      | Allure (`historyId()`, `testCaseId()`), ReportPortal (`testCaseId()`), trcli (`custom_automation_id`) | La clave sale del archivo y de los nombres, igual que la de `probara import junit`: vincula un caso por su ID ([claves de automatización](https://docs.probara.net/es/developers/ci-integrations/vitest/linking/#claves-de-automatización))                                                                                                                                                       |
| Un propietario                                                            | Allure (`owner()`)                                                                                    | Un caso de Probara no tiene campo de propietario: asigna en su lugar los resultados fallidos ([asignar resultados fallidos](https://docs.probara.net/es/developers/ci-integrations/vitest/results-and-retries/#asignar-resultados-fallidos))                                                                                                                                                      |
| Campos de resultado                                                       | trcli (`--result-fields`, `testrail_result_field`)                                                    | Los resultados de Probara no tienen campos personalizados: los parámetros (`probara.parameters()`) y los enlaces (`probara.link()`) llevan los datos adicionales                                                                                                                                                                                                          |
| Pasos y llamadas desde los hooks de una suite                             | Allure (una llamada en `beforeAll` va a cada prueba de la suite)                                      | Una llamada donde no corre una sola prueba no pertenece a ninguna prueba: se deja fuera con una advertencia, no se copia en cada una. Crea los pasos importantes en la prueba o en su `beforeEach`; un hook que lanza un error se sigue mostrando ([errores fuera de una prueba](https://docs.probara.net/es/developers/ci-integrations/vitest/results-and-retries/#errores-fuera-de-una-prueba)) |
| Un archivo de configuración                                               | Qase (`qase.config.json`, `.qaserc`), trcli (`config.yml`)                                            | Un archivo con un token podría subirse al repositorio: el token se queda en el entorno, y las opciones en la configuración de Vitest ([configuración](https://docs.probara.net/es/developers/ci-integrations/vitest/configuration/))                                                                                                                                                              |
| Benchmarks y cobertura                                                    | Vitest (`vitest bench`, `--coverage`)                                                                 | Probara no tiene resultados de benchmarks ni de cobertura: el reporter envía resultados de pruebas                                                                                                                                                                                                                                                                        |
| Pruebas de tipos                                                          | Vitest (`vitest typecheck`)                                                                           | Ninguna prueba del reporter las cubre, así que no se promete nada para ellas                                                                                                                                                                                                                                                                                              |
| Launches de repetición y de depuración                                    | ReportPortal (`rerun`, `rerunOf`, `mode: 'DEBUG'`)                                                    | Vuelve a reportar en la misma ejecución con `run.ulid`: cada caso de la ejecución conserva el último resultado y su historial. Para probar una configuración, [comprueba qué se enviaría](https://docs.probara.net/es/developers/ci-integrations/vitest/troubleshooting/#comprobar-qué-se-enviaría)                                                                                               |
| Iniciar sesión con OAuth                                                  | ReportPortal (`oauth`)                                                                                | El CI reporta con un token de aplicación de la organización, que no ocupa ningún puesto y sobrevive a la persona que lo creó                                                                                                                                                                                                                                              |
| Archivos y errores globales                                               | Allure (`globalAttachment()`, `globalError()`)                                                        | Los archivos y los errores pertenecen a un resultado: adjúntalos desde la prueba que los produjo                                                                                                                                                                                                                                                                          |
| Descripciones en HTML                                                     | Allure (`descriptionHtml()`)                                                                          | La descripción es texto; Probara le da formato                                                                                                                                                                                                                                                                                                                            |
| Listeners                                                                 | Allure (`listeners`)                                                                                  | Ningún hook cambia un resultado antes de enviarlo; `statusMapping` y `statusFilter` cubren los estados ([mapeo de estados](https://docs.probara.net/es/developers/ci-integrations/vitest/results-and-retries/#mapeo-y-filtro-de-estados))                                                                                                                                                         |
| Desactivar la verificación TLS                                            | trcli (`--insecure`)                                                                                  | No se puede desactivar: pasa a Node tu autoridad de certificación ([autoridades de certificación propias](https://docs.probara.net/es/developers/ci-integrations/vitest/configuration/#autoridades-de-certificación-propias))                                                                                                                                                                     |
| Deshacer una subida fallida                                               | trcli                                                                                                 | Un resultado registrado se conserva: deshacerlo perdería los que sí llegaron                                                                                                                                                                                                                                                                                              |
| Referencias en las ejecuciones                                            | trcli (`--test-run-ref`)                                                                              | La ejecución registra la URL del build de CI, la rama y el commit; las incidencias se vinculan a los resultados con `probara.issue()`                                                                                                                                                                                                                                     |

## Actualizar el reporter

`@probara/vitest-reporter` sigue el [versionado semántico](https://semver.org). Lee el
[changelog](https://github.com/cynch-me/probara-sdk/blob/main/packages/vitest-reporter/CHANGELOG.md)
antes de actualizarlo. Una versión mayor nueva de Vitest puede cambiar los nombres que da a las
pruebas, y con ellos sus claves de automatización: antes de actualizar Vitest, lee
[de Vitest 4 a 5](#de-vitest-4-a-5) y
[compara las claves](#el-contrato-de-la-clave-de-automatización).

- **0.x (ahora)**: el reporter es joven. Una versión menor (`0.2.0`) puede cambiar opciones, líneas
  del registro o valores por defecto; una versión de parche (`0.1.1`) solo corrige errores. Fija la
  versión en `package.json` (o conserva el lockfile) y actualiza a propósito.
- **1.0 y posteriores**: cambios incompatibles solo en una versión mayor.
- `@probara/vitest-reporter` depende de `@probara/core` de la misma línea de versiones; instala solo
  el reporter y deja que traiga su core.

### Qué es un cambio incompatible

Cualquiera de estos necesita una versión mayor a partir de la 1.0, y una menor antes:

- Una opción o una variable `PROBARA_*` eliminada o renombrada, o con otro significado.
- Un valor por defecto cambiado (cerrar ejecuciones, crear los casos que faltan, `keyIncludesFile`,
  lo que se adjunta).
- Un helper `probara.*` eliminado, o con otro comportamiento.
- Un estado con el que se envía un intento, cambiado para el mismo intento.
- **Cualquier cambio en la clave de automatización que recibe una prueba**, con `keyIncludesFile`
  activado o desactivado. Una clave distinta desvincula la prueba de su caso: la siguiente ejecución
  crea uno nuevo.
- Dejar de admitir una versión de Vitest o de Node.js que sigue teniendo soporte de sus autores.

Añadir una opción, un helper, una línea de registro o un archivo que se sube no es incompatible. Las
líneas del registro en stderr son para personas: no las analices con código.

### El contrato de la clave de automatización

El algoritmo de la clave es la
[clave de automatización v1](https://github.com/cynch-me/probara-sdk/blob/main/packages/core/README.md#the-automation-key-v1),
congelada en `@probara/core` y fijada con vectores de referencia: ninguna versión del reporter la
cambia. Lo que añade el reporter es cómo una prueba de Vitest se convierte en las partes de la
clave, igual que `probara import junit` lee la salida JUnit de Vitest
([claves](https://docs.probara.net/es/developers/ci-integrations/vitest/linking/#claves-de-automatización)):

- el archivo de prueba relativo a `rootDir` (la raíz de Vitest), cuando `keyIncludesFile` está
  activado;
- los títulos de los `describe` y el de la prueba, cada uno una parte, como los nombra la salida
  JUnit de Vitest, sin los IDs de caso;
- una fila de `test.each` o `test.for`, por el título que le da el formato de Vitest.

Esa correspondencia también forma parte del contrato: una versión del reporter que cambia la clave
de una prueba es un cambio incompatible, señalado como tal en el changelog. Una versión de Vitest
que formatea los títulos de otra forma también cambia claves (abajo), tanto para el reporter como
para `probara import junit`.

Para comprobar una actualización de cualquiera de los dos, compara lo que enviarían ambas versiones:
ejecuta la batería con el envío desactivado una vez por versión, cada una con su propio archivo de
resultados, y compara las simulaciones con `diff`
([comprobar qué se enviaría](https://docs.probara.net/es/developers/ci-integrations/vitest/troubleshooting/#comprobar-qué-se-enviaría)).
Cualquier clave que cambie es una prueba que recibiría un caso nuevo.

### Versiones de Vitest y de Node.js

| Necesita       | Versiones admitidas                                                                                    |
| -------------- | ------------------------------------------------------------------------------------------------------ |
| `vitest`       | 3.2 y posteriores (el rango de peer `^3.2.0 \|\| ^4.0.0 \|\| ^5.0.0`): probado en 3.2.7, 4.1.11, 5.0.2 |
| Modo navegador | El proveedor de tu versión de `vitest`: probado en 3.2.7, 4.0.18, 4.1.11 y 5.0.2                       |
| Node.js        | 22.12 y posteriores (el campo `engines`)                                                               |

Vitest 3.2 es la primera versión con anotaciones de prueba, que el reporter lee, de ahí el rango de
peer. El modo navegador tiene su propia configuración por cada línea de Vitest
([modo navegador](https://docs.probara.net/es/developers/ci-integrations/vitest/browser-mode/)). `0.1.0` es la primera
versión del reporter: todavía no hay nada desde lo que migrar, y si vienes de `probara import junit`
sobre la salida JUnit de Vitest, las claves se mantienen.

### De Vitest 4 a 5

**Cambian algunos títulos de `test.each` y `test.for`**, y con ellos sus claves. Vitest 5 formatea
de otra manera los valores que pone en un título:

| La prueba                                                         | Vitest 3 y 4                                                          | Vitest 5                                         |
| ----------------------------------------------------------------- | --------------------------------------------------------------------- | ------------------------------------------------ |
| `test.each([{ name: 'Ana' }])('greets $name')`                    | `greets 'Ana'`                                                        | `greets Ana`                                     |
| `test.each([{ note }])('saves $note')`, una nota de 48 caracteres | `saves 'Leave the parcel with the neighbour a…'`                      | `saves Leave the parcel with the neighbour at …` |
| `test.each([[{ note }]])('stores %o')`                            | `stores { note: 'Leave the parcel with the neighbour at number 12' }` | `stores { …(1) }`                                |
| `test.each([[new Map([['eur', 12]])]])('reads %o')`               | `reads Map{ 'eur' => 12 }`                                            | `reads Map { 'eur' => 12 }`                      |
| Números, objetos y arrays cortos, `%s` de una cadena              | Igual                                                                 | Igual                                            |

Las claves de esas filas, tal como las nombra Vitest 5:

```ts
// tests/rows.test.ts
import { test } from 'vitest';

const note = 'Leave the parcel with the neighbour at number 12';

test.each([{ name: 'Ana' }])('greets $name', () => {});

test.each([[{ note }]])('stores %o', () => {});
```

```json
[
  { "automationKey": "tests/rows.test.ts > greets Ana" },
  { "automationKey": "tests/rows.test.ts > stores { …(1) }" }
]
```

- **Una clave que cambia es un caso nuevo.** El caso anterior conserva su historial, y los
  resultados de la clave nueva van a un caso nuevo. Compara las claves antes de actualizar, como
  arriba: cada línea que cambia es una de esas filas.
- **Las filas pueden coincidir.** `{ …(1) }` representa cualquier objeto de una sola clave demasiado
  largo para imprimirse, así que dos filas así reciben el mismo título y comparten un caso. Pon en
  el título un valor corto de cada fila (`'stores $id'`).
- **`probara import junit` cambia del mismo modo**: lee los nombres que Vitest 5 escribe en su
  salida JUnit, así que los dos caminos siguen dando a una prueba la misma clave.
- **Las carpetas por defecto cambian**: `attachmentsDir` pasa de `.vitest-attachments` a
  `.vitest/attachments`
  ([dónde se guardan las copias](https://docs.probara.net/es/developers/ci-integrations/vitest/steps-and-attachments/#dónde-se-guardan-las-copias)),
  y los blobs de un job con sharding pasan de `.vitest-reports` a `.vitest/blob`, que es lo que lee
  `vitest --merge-reports` por defecto. Un job de CI que los lleva, o que los limpia, necesita las
  rutas nuevas
  ([lleva los blobs y los adjuntos](https://docs.probara.net/es/developers/ci-integrations/vitest/sharding/#lleva-los-blobs-y-los-adjuntos)).

### De Vitest 3 a 4

- **El modo navegador** tiene un paquete de proveedor nuevo, `@vitest/browser-playwright`, con
  `provider: playwright()` ([modo navegador](https://docs.probara.net/es/developers/ci-integrations/vitest/browser-mode/)).
- **Las etiquetas de Vitest** (4.1 y posteriores) se suman a las etiquetas de los casos que crea un
  informe; Vitest rechaza una etiqueta que su configuración no declara
  ([etiquetas de Vitest](https://docs.probara.net/es/developers/ci-integrations/vitest/metadata/#etiquetas-de-vitest)).
- **Los cuerpos de texto de `context.annotate()`** necesitan `bodyEncoding: 'utf-8'` a partir de
  Vitest 4.1.3: si no, Vitest marca el cuerpo como base64
  ([anotaciones de Vitest](https://docs.probara.net/es/developers/ci-integrations/vitest/steps-and-attachments/#anotaciones-de-vitest)).

### Cualquier actualización de un pipeline con sharding

`vitest --merge-reports` solo lee los blobs de su propia versión de Vitest: actualiza a la vez los
shards y el job que fusiona, en un único cambio del pipeline
([la misma versión de Vitest en todas partes](https://docs.probara.net/es/developers/ci-integrations/vitest/sharding/#la-misma-versión-de-vitest-en-todas-partes)).

Relacionado: [Primeros pasos](https://docs.probara.net/es/developers/ci-integrations/vitest/) ·
[Vincular](https://docs.probara.net/es/developers/ci-integrations/vitest/linking/) · [CLI](https://docs.probara.net/es/developers/cli/) · Fuente:
[migrating-from-junit.md](https://github.com/cynch-me/probara-sdk/blob/main/packages/vitest-reporter/docs/migrating-from-junit.md),
[migrating-from-qase.md](https://github.com/cynch-me/probara-sdk/blob/main/packages/vitest-reporter/docs/migrating-from-qase.md),
[coming-from-other-tools.md](https://github.com/cynch-me/probara-sdk/blob/main/packages/vitest-reporter/docs/coming-from-other-tools.md),
[upgrade.md](https://github.com/cynch-me/probara-sdk/blob/main/packages/vitest-reporter/docs/upgrade.md)
