# Migrar al reporter de Jest

> Pasa a @probara/jest-reporter desde la importación JUnit, Qase, Test IT, ReportPortal, Allure o TestRail conservando tus casos — y cómo actualizar el reporter con seguridad.

Vengas de donde vengas, el reporter es una entrada en `reporters`, con un archivo de setup opcional,
y los helpers `probara.*` funcionan en cada worker de Jest, en banda, en los entornos `node` y
`jsdom`, y en modo watch. Conservas tu propio `testEnvironment`.

```js
// jest.config.js
module.exports = {
  reporters: ['default', ['@probara/jest-reporter', { projectId: 'SHOP' }]],
  setupFilesAfterEnv: ['@probara/jest-reporter/setup'],
};
```

El archivo de setup solo hace falta para
[`captureOutput`](https://docs.probara.net/es/developers/ci-integrations/jest/steps-and-attachments/#salida-de-consola) y
para la [selección de pruebas](https://docs.probara.net/es/developers/ci-integrations/jest/run-selection/).

| Vienes de                           | Ve a                                                |
| ----------------------------------- | --------------------------------------------------- |
| jest-junit y `probara import junit` | [La importación JUnit](#desde-la-importación-junit) |
| `jest-qase-reporter`                | [Qase](#desde-qase)                                 |
| `testit-adapter-jest`               | [Test IT](#desde-test-it)                           |
| `@reportportal/agent-js-jest`       | [ReportPortal](#desde-reportportal)                 |
| `allure-jest`                       | [Allure](#desde-allure)                             |
| jest-junit y `trcli parse_junit`    | [TestRail](#desde-testrail)                         |

## Desde la importación JUnit

Si tu pipeline escribe un informe con [jest-junit](https://github.com/jest-community/jest-junit) y lo
envía con `probara import junit`, el reporter puede sustituir ambos pasos **sin desvincular ni un
solo caso**: da a cada prueba la clave de automatización que le da la importación. Un ajuste decide
qué claves: si jest-junit escribió el archivo de prueba en el informe.

1. Averigua cómo se ejecutaba jest-junit en tu pipeline: con `JEST_JUNIT_ADD_FILE_ATTRIBUTE=true` (o
   `addFileAttribute: 'true'` en sus opciones), o sin él (su valor por defecto).
2. Añade el reporter a la configuración de Jest. Sin el atributo de archivo, define también
   `keyIncludesFile: false`.
3. [Comprueba las claves](#comprueba-las-claves-antes-de-cambiar) de ambos con las mismas pruebas:
   deben ser iguales.
4. En CI, elimina jest-junit y el paso de importación, y pasa `PROBARA_API_TOKEN` al paso de pruebas.

| jest-junit escribió                                           | Ajuste del reporter                                    |
| ------------------------------------------------------------- | ------------------------------------------------------ |
| El atributo de archivo (`JEST_JUNIT_ADD_FILE_ATTRIBUTE=true`) | Nada: `keyIncludesFile: true` es el valor por defecto  |
| Sin atributo de archivo (el valor por defecto de jest-junit)  | `keyIncludesFile: false` (`PROBARA_KEY_INCLUDES_FILE`) |

Las claves son las que da la importación con las **plantillas por defecto** de jest-junit: los bloques
`describe` y el título unidos con espacios (`{classname} {title}`). Un informe escrito con otras
plantillas (`classNameTemplate`, `titleTemplate`, `ancestorSeparator`, `suiteNameTemplate`) tiene
otras claves: la comprobación de abajo muestra la diferencia, y los IDs en los títulos mantienen esas
pruebas vinculadas sean cuales sean sus claves.

### Comprueba las claves antes de cambiar

Ejecuta la batería una vez con jest-junit y otra con el 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. Para este archivo de prueba:

```js
// tests/refund.test.js
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
$ JEST_JUNIT_ADD_FILE_ATTRIBUTE=true npx jest --reporters=default --reporters=jest-junit
$ npx @probara/cli import junit junit.xml --dry-run
passed	SHOP-12	tests/refund.test.js > refund refunds an order
passed	-	tests/refund.test.js > refund keeps the receipt
passed	-	tests/refund.test.js > refund refunds a gift card
Total: 3 results from 1 file (3 passed, 0 failed, 0 skipped, 0 blocked)
$ PROBARA_ENABLED=false PROBARA_RESULTS_FILE=probara-results.json npx jest
[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.js > refund refunds an order
passed	-	tests/refund.test.js > refund keeps the receipt
skipped	-	tests/refund.test.js > refund refunds a gift card
Total: 3 results from 1 file (2 passed, 0 failed, 1 skipped, 0 blocked)
```

Las claves y los casos son los mismos; solo difiere el estado del `test.todo` (consulta
[qué cambia](#qué-cambia-respecto-a-la-importación-junit)). En una batería real, escribe cada
simulación en un archivo y compáralas con `diff`: una línea cuya clave cambió es una prueba que
recibiría un caso nuevo. Borra después `junit.xml` y el archivo de resultados.

### En CI

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

```yaml
- name: Run Jest tests
  run: npx jest --ci --reporters=default --reporters=jest-junit
  env:
    JEST_JUNIT_ADD_FILE_ATTRIBUTE: 'true'
- name: Send the results
  if: ${{ !cancelled() }}
  run: npx @probara/cli import junit junit.xml
  env:
    PROBARA_API_TOKEN: ${{ secrets.PROBARA_API_TOKEN }}
    PROBARA_PROJECT: SHOP
```

Después, un paso: el reporter, registrado en la configuración de Jest, envía cada resultado al final
del paso de pruebas.

```yaml
- name: Run Jest tests
  run: npx jest --ci
  env:
    PROBARA_API_TOKEN: ${{ secrets.PROBARA_API_TOKEN }}
    PROBARA_PROJECT: SHOP
```

Quita `--reporters=jest-junit` (sustituiría los reporters de la configuración, incluido el
reporter), y el propio jest-junit salvo que otra cosa lea su informe. La ejecución recibe el nombre,
el origen y el cierre como lo hacía la importación.

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

| Tema                                    | `probara import junit` sobre jest-junit                                                 | El reporter                                                                                                  |
| --------------------------------------- | --------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------ |
| `test.todo`                             | `passed`: jest-junit escribe un todo como un testcase aprobado                          | `skipped`, con la nota `Todo`: una prueba aún no escrita nunca cuenta como aprobada                          |
| Reintentos (`jest.retryTimes`)          | Solo el último intento                                                                  | Cada intento, en orden; el caso de la ejecución conserva el último resultado                                 |
| Pruebas excluidas por `-t` o `.only`    | `skipped`                                                                               | `skipped` también                                                                                            |
| Un hook `afterAll` que falla            | Un testcase fallido más para el archivo                                                 | El mismo resultado fallido, con la misma clave                                                               |
| Un archivo que no se carga              | Nada (jest-junit no escribe nada para él por defecto)                                   | Nada que reportar, y una advertencia nombra el archivo                                                       |
| Cuándo se envían 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 guarda lo que no se pudo enviar |
| Suite de un caso creado, sin el archivo | El primer describe de la primera prueba del archivo, para todas las pruebas del archivo | El primer describe de cada prueba (ninguno para una prueba fuera de cualquier describe)                      |

La última fila solo cambia a dónde va un caso que **crea** el informe, con `keyIncludesFile: false`:
una prueba nueva en un archivo cuyas pruebas están en distintos describes de nivel superior. Su
clave, y por tanto el caso que encuentra una prueba existente, es la misma. Con el atributo de
archivo, ambos ponen un caso creado en una suite con el nombre del archivo.

El reporter sigue al pie de la letra la forma de nombrar de jest-junit, peculiaridades incluidas,
para que las claves sigan siendo iguales: `{displayName}` se convierte en el nombre del proyecto de
Jest, los patrones `$` de `String.replace` en un título se expanden como en las plantillas de
jest-junit, y un archivo cuyo `afterAll` lanza un error recibe el resultado fallido extra con la clave
`<file> > Test execution failure: could be caused by test hooks like 'afterAll'.`

Lo que ganas: cada intento de una prueba reintentada; pasos anidados; archivos y salida de consola;
enlaces e incidencias; parámetros y los campos de los casos nuevos; la selección de pruebas; y los
resultados fallidos asignados a las personas que indiques, como hacía `--assign-failed-to` en la
importación.

## Desde Qase

Pasar de `jest-qase-reporter` es sobre todo cambiar nombres: la entrada del reporter, sus ajustes y
las llamadas `qase.*`. Por el camino, una cosa mejora: los helpers `probara.*` funcionan en cada
worker de Jest, en `jsdom` y en modo watch, mientras que los de Qase solo funcionan en banda.

1. `npm uninstall jest-qase-reporter && npm i -D @probara/jest-reporter`.
2. En la configuración de Jest, sustituye `['jest-qase-reporter', {...}]` por
   `['@probara/jest-reporter', {...}]`, traduciendo las opciones (abajo). Borra `qase.config.json` y
   `.qaserc`: el reporter solo lee sus opciones y las variables `PROBARA_*`.
3. Sustituye en CI las variables `QASE_*` por las `PROBARA_*`: como mínimo `PROBARA_API_TOKEN` (un
   token de aplicación de la tarjeta **Jest**) y `PROBARA_PROJECT`.
4. Sustituye `require('jest-qase-reporter/jest')` por `require('@probara/jest-reporter')`, y traduce
   las llamadas (abajo). Los IDs de caso pasan a ser IDs visibles de Probara (`SHOP-12`), no números.
5. Quita `--runInBand` si lo añadiste solo por los helpers de Qase: los de Probara no necesitan nada.

Antes:

```js
// jest.config.js
module.exports = {
  reporters: [
    'default',
    [
      'jest-qase-reporter',
      {
        mode: 'testops',
        testops: {
          project: 'SHOP',
          run: { title: 'Nightly', complete: true, tags: ['nightly'] },
          uploadAttachments: true,
        },
      },
    ],
  ],
};
```

Después:

```js
// jest.config.js
module.exports = {
  reporters: [
    'default',
    [
      '@probara/jest-reporter',
      { projectId: 'SHOP', run: { name: 'Nightly', tags: ['nightly'] }, captureOutput: true },
    ],
  ],
  setupFilesAfterEnv: ['@probara/jest-reporter/setup'],
};
```

`captureOutput` y su archivo de setup son opcionales: adjuntan la salida de consola de cada prueba,
algo que `captureLogs` de Qase nunca hizo para Jest.

### 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`)                                                |
| `mode: 'testops_multi'`, `testops_multi.*`                                 | `projectId` más `projects` y `run.ulids`                                              |
| `debug` (`QASE_DEBUG`)                                                     | `debug` (`PROBARA_DEBUG`)                                                             |
| `environment` (`QASE_ENVIRONMENT`), un slug, ignorado si no existe         | `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 Jest de Qase lo ignora | `captureOutput` (`PROBARA_CAPTURE_OUTPUT`), con el archivo de setup                   |
| `statusMapping` (`QASE_STATUS_MAPPING`)                                    | `statusMapping` (`PROBARA_STATUS_MAPPING`), sin `invalid` ni `disabled`               |
| `logging.console`, `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` (segundos)                                           | `timeoutMs` (milisegundos), para cada petición                                        |
| `testops.api.retries`, `testops.api.retryBackoff`                          | `maxRetries`, para cada petición; el backoff es fijo                                  |
| `testops.project` (`QASE_TESTOPS_PROJECT`)                                 | `projectId` (`PROBARA_PROJECT`)                                                       |
| `testops.uploadAttachments`                                                | `uploadAttachments` (`PROBARA_UPLOAD_ATTACHMENTS`)                                    |
| `testops.attachments.concurrency`, `.timeout`                              | `attachmentConcurrency`; las subidas tienen al menos 120 s                            |
| `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`); `skipped` también descarta las pruebas todo |
| `testops.batch.size` (`QASE_TESTOPS_BATCH_SIZE`)                           | `chunkSize` (de 1 a 500)                                                              |
| (ninguna)                                                                  | `run.milestone` (`PROBARA_MILESTONE`): el reporter de Qase no tiene hitos             |

### Las llamadas `qase.*`

| Qase                                                        | Probara                                                                           |
| ----------------------------------------------------------- | --------------------------------------------------------------------------------- |
| `test(qase(12, 'pays'), ...)`, `(Qase ID: 12)` en un título | `test('SHOP-12 pays', ...)`: un ID en el título o en un `describe`                |
| `test(qase([12, 13], 'pays'), ...)`                         | `test('SHOP-12 SHOP-13 pays', ...)`, o `probara.id(['SHOP-12', 'SHOP-13'])`       |
| `qase.projects({ WEB: [3] }, 'pays')`                       | `WEB-3` en el título, con `projects: ['WEB']`                                     |
| (ninguna: el reporter de Jest de Qase no tiene `qase.id()`) | `probara.id('SHOP-12')`                                                           |
| `qase.title('...')`                                         | `probara.title('...')`                                                            |
| `qase.suite('Payments')`                                    | `probara.suite('Payments')`, o `probara.suite(['Payments', 'Cards'])` para anidar |
| `qase.fields({ severity: 'critical' })`                     | `probara.fields({ severity: 'critical' })`                                        |
| `qase.tags('smoke')`                                        | `probara.tags('smoke')`                                                           |
| `qase.comment('...')`                                       | `probara.comment('...')`                                                          |
| `qase.ignore()`                                             | `probara.ignore()`                                                                |
| `qase.parameters({...})`, `qase.groupParameters({...})`     | `probara.parameters({...})`                                                       |
| `qase.attach({ paths: [a, b] })`                            | `probara.attach({ name, path })`, una vez por archivo                             |
| `qase.attach({ name, content, type })`                      | `probara.attach({ name, body, contentType })`                                     |
| `await qase.step('Pay', fn, 'Paid', 'card=visa')`           | `await probara.step('Pay', fn, { expected: 'Paid', data: 'card=visa' })`          |
| `s.step(...)`, `s.attach(...)` dentro del cuerpo de un paso | `probara.step(...)`, `probara.attach(...)` dentro del cuerpo: se anidan solos     |

Antes:

```js
// tests/checkout.test.js
const { qase } = require('jest-qase-reporter/jest');

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

Después:

```js
// tests/checkout.test.js
const { probara } = require('@probara/jest-reporter');

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.js > 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 ID sale del título y de la clave: añadirlo o quitarlo nunca cambia qué caso encuentra la prueba.

### Los helpers funcionan en cada worker

Las llamadas `qase.*` de Qase llegan a su reporter a través de una variable global del proceso
principal de Jest. Esa global solo existe en una prueba cuando Jest se ejecuta en banda **y** el
entorno de prueba es `node`: con workers (lo que usa Jest por defecto con más de un archivo), en
`jsdom`, y siempre en modo watch, las llamadas lanzan un error y hacen fallar la prueba. Además, Jest
decide por su cuenta si ejecuta en banda o con workers, según los tiempos de ejecuciones anteriores,
así que la misma batería puede pasar un día y fallar al siguiente.

Los helpers de Probara escriben en un canal al que llega cada worker, y cada llamada identifica su
prueba por su archivo, su nombre completo y su intento. Funcionan en workers, en banda, en `node` y
`jsdom`, en modo watch y en pruebas `test.concurrent`; una llamada fuera de una prueba en curso se
omite con una advertencia, nunca se lanza como error.

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

| Tema                                   | Qase                                                                                                                                            | Probara                                                                                                                                             |
| -------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
| Precedencia                            | Su código hace que las variables ganen a `qase.config.json`, que gana a las opciones del reporter (su documentación dice lo contrario)          | Opciones en la configuración de Jest, después variables `PROBARA_*`, después 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 acabar en el repositorio. Las opciones viven en la configuración de Jest                   |
| 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                                    |
| Atribución de metadatos                | Por tiempos: las llamadas van a la siguiente prueba que reporta (las de `beforeAll` a la primera, las de `afterAll` a la del archivo siguiente) | Por identidad: cada llamada pertenece a la prueba que la ejecuta; fuera de una prueba se omite con una advertencia                                  |
| Pasos                                  | Planos, los hijos antes que su padre; el resultado esperado y los datos codificados en el nombre del paso                                       | Anidados tal como se ejecutaron, cada uno con su estado, duración y error; `expected` y `data` como opciones                                        |
| Adjuntos de pasos                      | Solo a través del `s.attach()` del cuerpo del paso; `qase.attach()` siempre va a la prueba                                                      | `probara.attach()` dentro de un paso va a ese paso                                                                                                  |
| `test.failing`                         | Un cuerpo que lanza un error pasa; un cuerpo que pasa suele quedar `invalid`                                                                    | El veredicto de Jest: un cuerpo que lanza un error pasa, un cuerpo que pasa falla                                                                   |
| 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                               |
| `test.todo`                            | Omitido, pero `statusFilter` necesita `disabled` para descartarlo                                                                               | Omitido, con la nota `Todo`; `statusFilter: ['skipped']` lo descarta                                                                                |
| Pruebas excluidas por `-t` o `.only`   | Se reportan como omitidas                                                                                                                       | También se reportan como omitidas: Jest las reporta así, y el reporter envía lo que reporta Jest                                                    |
| Un `afterAll` que falla                | No se reporta: las pruebas siguen en verde en Qase                                                                                              | Un resultado fallido más para el archivo, el que escribe jest-junit                                                                                 |
| Un archivo que no se carga             | No se reporta ni se dice nada                                                                                                                   | Nada que reportar (no se ejecutó ninguna prueba), y una advertencia nombra el archivo                                                               |
| `qase.comment()`                       | Sustituye el mensaje de error de un resultado fallido                                                                                           | Se escribe al principio de las notas, antes del error, que se conserva                                                                              |
| 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                                                     |
| Cierre de la ejecución                 | Solo con `run.complete: true`; sin definir la deja abierta                                                                                      | Una ejecución que crea el reporter se cierra al final; una reutilizada queda abierta                                                                |
| Shards                                 | Cada shard crea su propia ejecución salvo que reciba un ID de ejecución                                                                         | `probara run create`, cada shard con `PROBARA_RUN_ULID`, y después `probara run close`                                                              |
| Modo watch                             | Una ejecución nueva por repetición; los helpers lanzan errores (nunca en banda)                                                                 | Una ejecución para toda la sesión, que queda abierta                                                                                                |
| Cuando falla el envío                  | `fallback` a un informe local, de forma definitiva, desde el primer error                                                                       | Un archivo de resultados, que `probara import results` envía después a las mismas ejecuciones                                                       |
| Strings en `attach()`                  | Un string que parece base64 se decodifica (`'Hello'` sube basura)                                                                               | Un string se envía tal cual; pasa un `Buffer` para contenido binario                                                                                |
| `attach({ paths })`                    | Debe ser un array (un string se lee carácter a carácter); el tipo de contenido es `type`, no `contentType`                                      | Un archivo por llamada, `path` o `body`, y `contentType`                                                                                            |
| El final de la ejecución               | No se espera: `--forceExit` puede cortar los últimos resultados                                                                                 | Jest espera al reporter: cada resultado se envía antes de que Jest salga                                                                            |
| Ejecutar solo las pruebas de un plan   | Una CLI externa (`qasectl`) construye un filtro `-t`                                                                                            | `runCasesOnly` ejecuta solo las pruebas de los casos de una ejecución                                                                               |

### 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 compartición de 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']`)                                  | Registra las llamadas HTTP del proceso principal, lo que en Jest significa solo en banda, y es ruido; adjunta en su lugar lo que importe                                      |
| Enlace de Jira en las ejecuciones (`run.externalLink`)                        | Probara vincula las incidencias de Jira a casos y defectos, que es donde vive la trazabilidad; `probara.issue()` vincula un resultado a una incidencia                        |
| `qase.mute()`                                                                 | Qase lo documenta, pero no existe en su reporter                                                                                                                              |
| 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 tener 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 y pásala en `run.ulids` |

## Desde Test IT

`testit-adapter-jest` es un entorno de prueba (`testEnvironment: 'testit-adapter-jest'`) con un
`globalSetup` y un `globalTeardown`: cada worker envía sus propios resultados a Test IT, archivo de
prueba por archivo de prueba. Sustituye los tres por la entrada del reporter del principio de esta
página.

```js
// jest.config.js (before)
module.exports = {
  testEnvironment: 'testit-adapter-jest',
  globalSetup: 'testit-adapter-jest/dist/globalSetup.js',
  globalTeardown: 'testit-adapter-jest/dist/globalTeardown.js',
  testEnvironmentOptions: {
    url: 'https://testit.example.com',
    projectId: '5236eb3f-7c05-46f9-a609-dc0278896464',
    adapterMode: 2,
    testRunName: 'Nightly',
  },
};
```

| Ajuste de Test IT (variable)                              | Probara (variable)                                                                      |
| --------------------------------------------------------- | --------------------------------------------------------------------------------------- |
| `url` (`TMS_URL`)                                         | `baseUrl` (`PROBARA_BASE_URL`), solo para un Probara autoalojado                        |
| `privateToken` (`TMS_PRIVATE_TOKEN`)                      | `PROBARA_API_TOKEN`: un token de aplicación de la tarjeta **Jest**                      |
| `projectId` (`TMS_PROJECT_ID`), un UUID                   | `projectId` (`PROBARA_PROJECT`), el ID del Proyecto (`SHOP`)                            |
| `configurationId` (`TMS_CONFIGURATION_ID`)                | `run.configurations` (`PROBARA_CONFIGURATIONS=Browser=Chrome`), por nombre              |
| `adapterMode: 2`, una ejecución nueva                     | El valor por defecto: cada `jest` crea una ejecución y la cierra al final               |
| `adapterMode: 1` y `testRunId` (`TMS_TEST_RUN_ID`)        | `run.ulid` (`PROBARA_RUN_ULID`): reportar en una ejecución existente, que queda abierta |
| `testRunName` (`TMS_TEST_RUN_NAME`)                       | `run.name` (`PROBARA_RUN_NAME`), solo para una ejecución nueva                          |
| `testRunTags` (`TMS_TEST_RUN_TAGS`)                       | `run.tags` (`PROBARA_RUN_TAGS`)                                                         |
| `automaticCreationTestCases`                              | `createMissingCases` (`PROBARA_CREATE_MISSING_CASES`), activado por defecto             |
| `syncStorageEnabled`, `syncStoragePort`, `importRealtime` | Nada que definir: cada resultado pasa por el reporter en el proceso principal de Jest   |
| `LOG_LEVEL=debug`                                         | `debug` (`PROBARA_DEBUG`)                                                               |

| Llamada de Test IT (`testit.*`)               | Probara (`probara.*`)                                                           |
| --------------------------------------------- | ------------------------------------------------------------------------------- |
| `workItemIds(['1042'])`, `workItemId('1042')` | `id('SHOP-12')`, o `SHOP-12` en el título o en un `describe`                    |
| `displayName('...')`, `title('...')`          | `title('...')`: el título del caso que crea un informe                          |
| `description('...')`                          | `fields({ description: '...' })`                                                |
| `labels(['smoke'])`, `tags(['smoke'])`        | `tags('smoke')`                                                                 |
| `layer('API')`                                | `fields({ layer: 'API' })`, un campo de sistema del caso                        |
| `namespace('...')`, `classname('...')`        | `suite(['...', '...'])`: la ruta de suites del caso que crea un informe         |
| `params({ user: 'ana' })`                     | `parameters({ user: 'ana' })`                                                   |
| `step('Pay', 'description')`                  | `step('Pay', () => {...}, { expected: '...' })`: anidado, con estado y duración |
| `addAttachments(['a.png'])`                   | `attach({ name: 'a.png', path: 'a.png' })`, una vez por archivo                 |
| `addAttachments('text', 'log.txt')`           | `attach({ name: 'log.txt', body: 'text' })`                                     |
| `addLinks({ url, title })`, `links([...])`    | `link(url, title)`, una vez por enlace                                          |
| `addMessage('...')`                           | `comment('...')`                                                                |

| Tema                                  | Test IT                                                                                                                 | Probara                                                                                              |
| ------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- |
| Identidad                             | Un hash del archivo y del título de la propia prueba: dos pruebas con el mismo título en distintos describes colisionan | El archivo, cada describe y el título                                                                |
| Pruebas omitidas y `test.todo`        | No se reportan                                                                                                          | Se reportan como omitidas (`Todo` en las notas de un todo)                                           |
| Pasos                                 | Planos, sin estado ni duración                                                                                          | Anidados, cada uno con su estado, su duración y su error                                             |
| `test.concurrent`                     | Un búfer por archivo de prueba: las pruebas concurrentes sobrescriben los datos de las demás                            | Cada llamada pertenece a la prueba que la hizo                                                       |
| Cierre de la ejecución                | El adaptador nunca la cierra                                                                                            | Una ejecución que crea el reporter se cierra al final; una reutilizada queda abierta                 |
| Nombre de una ejecución existente     | Se renombra a `testRunName`                                                                                             | Nunca cambia: las opciones de una ejecución nueva se ignoran en una reutilizada, con una advertencia |
| Precedencia                           | Depende del tipo: los strings primero desde las opciones, los booleanos primero desde `tms.config.json`                 | Opciones, después variables `PROBARA_*`, después valores por defecto, para cada ajuste               |
| Ejecutar las pruebas de una ejecución | Una CLI externa escribe un filtro `-t`                                                                                  | `runCasesOnly` lee por sí mismo los casos de la ejecución                                            |

Lo que no se porta de Test IT:

| Test IT                                                   | Por qué                                                                                                                                     |
| --------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
| `automaticUpdationLinksToTestCases`, actualizar autotests | Un informe nunca cambia un caso existente: lo que está escrito ahí es de las personas                                                       |
| `testit.externalId()`                                     | La clave sale del archivo y de los nombres, igual que la de `probara import junit`; vincula un caso por su ID                               |
| Pasos de setup y teardown desde hooks                     | Los hooks son cómo se ejecuta una prueba, no lo que especifica; envuelve lo importante en `probara.step()`                                  |
| `testRunLinks` (enlaces en la ejecución)                  | La ejecución registra por sí sola la URL de su build de CI, su rama y su commit; los enlaces pertenecen a los resultados (`probara.link()`) |
| `certValidation: false`                                   | La verificación TLS no se puede desactivar; pasa en su lugar tu autoridad de certificación a Node                                           |

## Desde ReportPortal

`@reportportal/agent-js-jest` es un reporter, como este. Cambia la entrada y sus opciones:

```js
// jest.config.js (before)
module.exports = {
  reporters: [
    'default',
    [
      '@reportportal/agent-js-jest',
      { endpoint: 'https://rp.example.com/api/v1', project: 'shop', launch: 'Nightly' },
    ],
  ],
};
```

| ReportPortal (variable)                         | Probara (variable)                                                                                        |
| ----------------------------------------------- | --------------------------------------------------------------------------------------------------------- |
| `apiKey` (`RP_API_KEY`, `RP_TOKEN`)             | `PROBARA_API_TOKEN`                                                                                       |
| `endpoint` (`RP_ENDPOINT`)                      | `baseUrl` (`PROBARA_BASE_URL`), la URL de la aplicación sin ruta                                          |
| `project` (`RP_PROJECT_NAME`)                   | `projectId` (`PROBARA_PROJECT`)                                                                           |
| `launch` (`RP_LAUNCH`)                          | `run.name` (`PROBARA_RUN_NAME`)                                                                           |
| `description` (`RP_DESCRIPTION`)                | `run.description` (`PROBARA_RUN_DESCRIPTION`)                                                             |
| `attributes` (`RP_ATTRIBUTES=build:42,nightly`) | `run.tags` (`PROBARA_RUN_TAGS=build:42,nightly`): las etiquetas son nombres simples, dos puntos incluidos |
| `launchId` (`RP_LAUNCH_ID`)                     | `run.ulid` (`PROBARA_RUN_ULID`)                                                                           |
| `isLaunchMergeRequired`, `launchUuidPrint`      | `probara run create` imprime el ULID en el que reporta cada shard                                         |
| `restClientConfig` (timeout, retries)           | `timeoutMs`, `maxRetries`                                                                                 |
| `debug`                                         | `debug` (`PROBARA_DEBUG`)                                                                                 |
| `ReportingApi.attachment(file, description)`    | `probara.attach({ name, path })` o `{ name, body, contentType }`                                          |

| Tema                     | ReportPortal                                                                                                                                         | Probara                                                          |
| ------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------- |
| API dentro de la prueba  | Solo `attachment`, y solo en banda: vive en el proceso del reporter                                                                                  | Cada helper, en cada worker                                      |
| Reintentos               | Cada intento, marcado como `retry` cuando su referencia de código ya se vio antes en el proceso (también repeticiones en watch y nombres duplicados) | Cada intento es un resultado, sin marca: el historial lo muestra |
| `test.todo`              | Nunca se reporta                                                                                                                                     | Omitido, con la nota `Todo`                                      |
| Errores                  | El primer mensaje de fallo, como log y en la descripción                                                                                             | Cada error del intento, en las notas de su resultado             |
| Estructura               | Un elemento de suite por `describe`; el archivo no es un elemento                                                                                    | Cada caso en una suite con el nombre de su archivo               |
| Ajustes desde jest-junit | Su sección `jest-junit` de `package.json` y las variables `JEST_JUNIT_*` sustituyen sus opciones                                                     | Solo las opciones del reporter y las variables `PROBARA_*`       |

Lo que no se porta de ReportPortal:

| ReportPortal                                    | Por qué                                                                                                                  |
| ----------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------ |
| `skippedIssue` y el tipo de defecto `NOT_ISSUE` | Los problemas conocidos y la clasificación de defectos viven en Probara, en el resultado, no en un ajuste de CI          |
| `rerun`, `rerunOf`                              | Reporta en la misma ejecución con `run.ulid`: cada caso de la ejecución conserva el último resultado y todo su historial |
| Launches con `mode: 'DEBUG'`                    | Una ejecución de depuración es una ejecución: desactiva el envío en local (sin token), o comprueba qué se enviaría       |
| Inicio de sesión con OAuth                      | El CI reporta con un token de aplicación de la organización, que no ocupa puesto y sobrevive a la persona que lo creó    |

## Desde Allure

`allure-jest` es un entorno de prueba (`allure-jest/node`, `allure-jest/jsdom`) que escribe archivos
de resultados en `allure-results/`; un paso aparte los convierte en un informe. El reporter envía los
resultados a Probara al terminar la ejecución, y no necesita ningún entorno: vuelve a `node`, `jsdom`
o el tuyo propio.

```js
// jest.config.js (before)
module.exports = {
  testEnvironment: 'allure-jest/node',
  testEnvironmentOptions: {
    resultsDir: 'allure-results',
    links: { issue: { urlTemplate: 'https://jira.example.com/browse/%s' } },
  },
};
```

| Ajuste de Allure          | Probara                                                                                      |
| ------------------------- | -------------------------------------------------------------------------------------------- |
| `resultsDir`              | Nada: los resultados van a Probara. Un archivo de resultados guarda lo que no se pudo enviar |
| `links.issue.urlTemplate` | `issueUrlTemplate` (`PROBARA_ISSUE_URL_TEMPLATE`)                                            |
| `environmentInfo`         | `run.environment` (un nombre) y el origen de la ejecución, detectado del CI                  |
| `ALLURE_TESTPLAN_PATH`    | `runCasesOnly` con `PROBARA_RUN_ULID`                                                        |

| Llamada de 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, type)`                | `attach({ name, body: content, contentType: type })`                                    |
| `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')`                                                                         |

| 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                                              |
| Un `afterEach` o `afterAll` que falla | La prueba sigue aprobada; solo el fixture del hook queda roto                                  | El veredicto de Jest: un `afterEach` que falla hace fallar su prueba, un `afterAll` que falla añade un resultado fallido para el archivo |
| Un plan de pruebas                    | Las pruebas excluidas se siguen ejecutando, hooks incluidos, y solo se dejan fuera del informe | `runCasesOnly` las omite antes de ejecutarlas, y no las reporta                                                                          |
| Adjuntos                              | Cada uno envuelto en un paso propio                                                            | En el resultado, o en el paso en el que se adjuntaron                                                                                    |
| Reintentos e historial                | Un archivo de resultado por intento; el generador del informe los agrupa                       | Cada intento es un resultado de la ejecución; el caso conserva su historial entre ejecuciones                                            |
| Modo watch                            | Los archivos de resultados se acumulan en `resultsDir`                                         | Una ejecución para toda la sesión                                                                                                        |
| Campos del caso                       | Etiquetas en cada resultado                                                                    | Campos y etiquetas del caso que crea un informe; un caso existente conserva los suyos                                                    |

Lo que no se porta de Allure:

| Allure                                                   | Por qué                                                                                                                                          |
| -------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
| `testCaseId()`, `historyId()`                            | La clave sale del archivo y de los nombres, igual que la de `probara import junit`; vincula un caso por su ID                                    |
| Fixtures de setup y teardown desde hooks                 | Los hooks son cómo se ejecuta una prueba, no lo que especifica; envuelve lo importante en `probara.step()`                                       |
| El estado `broken`                                       | Una suposición a partir del tipo de error; Probara mantiene cuatro estados y el error completo                                                   |
| `categories`, marcas de conocido, silenciado e inestable | La clasificación vive en Probara, en los resultados y los defectos; cada intento es un resultado, así que la inestabilidad se ve en el historial |
| `owner()`                                                | Un caso de Probara no tiene campo de propietario; asigna en su lugar los resultados fallidos (`assignFailedTo`)                                  |
| Parámetros enmascarados, ocultos y excluidos             | Los parámetros se muestran con el resultado y nunca forman parte de la clave: omite un secreto en lugar de enmascararlo                          |
| `logStep()` con estado fallido o roto                    | El estado de un paso es lo que hizo su cuerpo: un paso que lanza un error falla                                                                  |
| `globalAttachment()`, `globalError()`                    | Los archivos y los errores pertenecen a un resultado; adjúntalos desde la prueba que los produjo                                                 |
| `descriptionHtml()`                                      | La descripción es texto; Probara le da formato                                                                                                   |
| `listeners`                                              | Ningún hook cambia un resultado antes de enviarlo; `statusMapping` y `statusFilter` cubren los estados                                           |
| Etiquetas globales `ALLURE_LABEL_*`                      | Etiqueta la ejecución con `run.tags`; las etiquetas del caso salen de `probara.tags()` en la prueba                                              |

## Desde TestRail

TestRail no tiene reporter para Jest: un pipeline escribe JUnit XML con jest-junit, y después la CLI de
Python de TestRail, `trcli parse_junit`, lo envía al terminar la ejecución. El reporter sustituye
ambos pasos, y envía lo que jest-junit no puede escribir: pasos, archivos, comentarios y cada
intento.

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

Con el reporter, basta con el paso de pruebas:

```bash
npx jest --ci
```

| 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                    |
| `--close-run`                                 | El valor 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`                                                     |
| `--case-matcher auto` (`classname.name`)      | La clave de automatización: archivo, describes y título                                       |
| `-y` (crear secciones y casos)                | `createMissingCases`, activado por defecto; suites a partir 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                             |
| `-a`, `--assign` (correos)                    | `assignFailedTo` (`PROBARA_ASSIGN_FAILED_TO`)                                                 |
| `-b`, `--batch-size`                          | `chunkSize` (de 1 a 500)                                                                      |
| `-t`, `--timeout` (segundos, por petición)    | `timeoutMs` (milisegundos, por petición)                                                      |
| `--proxy`, `--noproxy`                        | `HTTPS_PROXY` y `NO_PROXY` con `NODE_USE_ENV_PROXY=1`                                         |
| `-v` (detallado)                              | `debug` (`PROBARA_DEBUG`)                                                                     |
| Propiedad `testrail_result_comment`           | `probara.comment()`                                                                           |
| Propiedad `testrail_attachment`               | `probara.attach()`                                                                            |
| Propiedad `testrail_result_step`              | `probara.step()`                                                                              |

jest-junit no escribe propiedades de testcase, así que las tres últimas filas estaban fuera del
alcance de Jest: los helpers las hacen posibles.

| Tema                      | trcli sobre jest-junit                                     | Probara                                                                            |
| ------------------------- | ---------------------------------------------------------- | ---------------------------------------------------------------------------------- |
| Pruebas omitidas          | Retest                                                     | Omitidas                                                                           |
| `test.todo`               | Aprobado (jest-junit lo escribe como aprobado)             | Omitido, con la nota `Todo`                                                        |
| Reintentos                | Solo el último intento                                     | Cada intento                                                                       |
| Describes anidados        | Sin soporte: una sección por test suite                    | 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        | Revierte la ejecución, los casos y las secciones que creó  | No se revierte nada; un archivo de resultados guarda lo que no se envió            |
| Shards                    | `add_run -f run.yml`, y después `-c run.yml` en cada shard | `probara run create`, `PROBARA_RUN_ULID` en cada shard, `probara run close`        |

Lo que no se porta de TestRail:

| trcli                                                          | Por qué                                                                                                                                                          |
| -------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `--update-existing-cases`, `--update-strategy`, `refs`         | Un informe nunca cambia un caso existente: lo que está escrito ahí es de las personas                                                                            |
| Sustituir `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                                                    |
| `--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 |
| `--test-run-ref`                                               | La ejecución registra la URL de su build de CI, su rama y su commit; las incidencias se vinculan desde los resultados (`probara.issue()`)                        |
| `--insecure`                                                   | La verificación TLS no se puede desactivar; pasa en su lugar tu autoridad de certificación a Node                                                                |
| Los parsers `bdd`, `multisuite` y `saucectl`, `quality_rating` | Jest no tiene escenarios Gherkin; una ejecución abarca un proyecto, y `projects` reparte por ID de caso                                                          |
| Revertir ante un fallo                                         | Un resultado registrado se conserva: revertir perdería los que sí llegaron                                                                                       |

## Actualizar el reporter

`@probara/jest-reporter` sigue el [versionado semántico](https://semver.org). Lee el
[changelog](https://github.com/cynch-me/probara-sdk/blob/main/packages/jest-reporter/CHANGELOG.md)
antes de actualizar: cada cambio que requiere una acción por tu parte aparece ahí primero.

- **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 de forma deliberada.
- **1.0 y posteriores**: cambios incompatibles solo en una versión mayor.
- `@probara/jest-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 requiere 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 (cierre de ejecuciones, creación de casos que faltan,
  `keyIncludesFile`, qué se adjunta).
- Un helper `probara.*` o el archivo de setup eliminado, o con otro comportamiento.
- Un cambio en el estado con el que se envía un intento 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 soportar una versión de Jest o de Node.js que sigue teniendo soporte oficial.

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

### 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 por vectores de referencia: ninguna versión del reporter la
cambia. Lo que añade el reporter es cómo una prueba de Jest se convierte en las partes de la clave,
de la misma forma en que `probara import junit` lee el informe de jest-junit con sus plantillas por
defecto:

- el archivo de prueba relativo a `rootDir` (la ruta real del directorio en el que se ejecuta
  `jest`), cuando `keyIncludesFile` está activado, como lo escribe jest-junit con
  `JEST_JUNIT_ADD_FILE_ATTRIBUTE=true`;
- los describes y el título, unidos por espacios y divididos por `›` (con sus espacios), sin los IDs
  de caso;
- `{displayName}` en un describe o un título, rellenado con el nombre del proyecto de Jest, y los
  patrones `$` (`$$`, `$&`) expandidos, como hacen las plantillas de jest-junit.

Esa correspondencia también forma parte del contrato: una versión que cambia la clave de una prueba es
un cambio incompatible, y el changelog lo indica como tal. Para comprobar una actualización, compara
lo que enviarían ambas versiones, cada ejecución con su propio archivo de resultados
([comprobar qué se enviaría](https://docs.probara.net/es/developers/ci-integrations/jest/troubleshooting/#comprobar-qué-se-enviaría)).
Cualquier clave que cambie es una prueba que recibiría un caso nuevo.

### Versiones de Jest y de Node.js

| Necesita  | Soportado                                                                               |
| --------- | --------------------------------------------------------------------------------------- |
| `jest`    | 29.6 y posteriores (el rango de peer): probado en 29.7 y en la última 30                |
| Node.js   | 22.12 y posteriores (el campo `engines`)                                                |
| Un runner | jest-circus, el de Jest por defecto, para los helpers `probara.*` y el archivo de setup |

Jest 29.6 es la primera versión que avisa a un reporter cuando empieza cada prueba, de ahí el rango de
peer. Algunos detalles dependen de la versión:

| Jest               | Qué cambia                                                                                                                                                                       |
| ------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 29                 | Un `test.todo` o una prueba omitida recibe la hora de inicio de su archivo. `jest.retryTimes` nunca reintenta una prueba `test.concurrent`                                       |
| 30.0 a 30.4        | Una prueba `test.concurrent` reintentada por `jest.retryTimes`: las llamadas `probara.*` de sus reintentos se unen a las de su primer intento (Jest 30.5 distingue cada intento) |
| 30.5 y posteriores | Cada intento de cada prueba, incluido `test.concurrent`, recibe sus propias llamadas `probara.*`                                                                                 |

Con `testRunner: 'jest-jasmine2'` las pruebas se reportan, pero las llamadas `probara.*` no registran
nada (la primera muestra una advertencia), `captureOutput` no adjunta nada, y `runCasesOnly` ejecuta y
reporta todas las pruebas, con una advertencia. `0.1.0` es la primera versión: todavía no hay nada
desde lo que migrar.

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