# Migrar al reporter de Playwright

> Pásate a @probara/playwright-reporter desde playwright-qase-reporter o desde probara import junit, opción por opción, y actualízalo con seguridad.

Esta página hace corresponder `playwright-qase-reporter` con el reporter de Probara opción por
opción y llamada por llamada, muestra cómo pasar de `probara import junit` sin perder ningún caso y
explica cómo se versiona el reporter.

## Desde Qase

Pasar de `playwright-qase-reporter` es sobre todo cuestión de renombrar: el registro del reporter,
los ajustes y las llamadas `qase.*`.

1. Cambia los paquetes:

   ```bash
   npm uninstall playwright-qase-reporter && npm i -D @probara/playwright-reporter
   ```

2. En `playwright.config`, sustituye `['playwright-qase-reporter', {...}]` por
   `['@probara/playwright-reporter', {...}]`, traduciendo las opciones ([abajo](#opciones-y-variables)).
   Borra `qase.config.json`: el reporter solo lee sus opciones y las variables `PROBARA_*`.
3. Sustituye las variables `QASE_*` por las `PROBARA_*` en CI: como mínimo `PROBARA_API_TOKEN` (un
   token de aplicación de la tarjeta **Playwright**, consulta
   [primeros pasos](https://docs.probara.net/es/developers/ci-integrations/playwright/#1-crea-un-token-de-aplicación)) y
   `PROBARA_PROJECT`.
4. Sustituye `import { qase } from 'playwright-qase-reporter'` por
   `import { probara } from '@probara/playwright-reporter'`, y traduce las llamadas
   ([abajo](#las-llamadas-qase)). Los IDs de caso pasan a ser IDs visibles de Probara (`SHOP-12`), no
   números.

Antes:

```ts
// playwright.config.ts
import { defineConfig } from '@playwright/test';

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

Después:

```ts
// playwright.config.ts
import { defineConfig } from '@playwright/test';

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

### Opciones y variables

| Opción de Qase (variable `QASE_*`)                    | Opción de Probara (variable `PROBARA_*`)                                                                     |
| ----------------------------------------------------- | ------------------------------------------------------------------------------------------------------------ |
| `mode: 'testops'` (`QASE_MODE`)                       | Activo siempre que haya un token y un proyecto                                                               |
| `mode: 'off'`                                         | `enabled: false` (`PROBARA_ENABLED=false`), o sin token                                                      |
| `mode: 'report'`, `fallback`                          | `resultsFile` (`PROBARA_RESULTS_FILE`)                                                                       |
| `mode: 'testops_multi'`, `testops_multi.*`            | `projectId` junto con `projects` y `run.ulids`                                                               |
| `debug` (`QASE_DEBUG`)                                | `debug` (`PROBARA_DEBUG`)                                                                                    |
| `environment` (`QASE_ENVIRONMENT`), un slug           | `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`)                   | `captureOutput` (`PROBARA_CAPTURE_OUTPUT`)                                                                   |
| `statusMapping` (`QASE_STATUS_MAPPING`)               | `statusMapping` (`PROBARA_STATUS_MAPPING`), sin `invalid`                                                    |
| `logging.console`, `logging.file`                     | El registro va 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)                                                                                   |
| `testops.api.retries`, `testops.api.retryBackoff`     | `maxRetries`; la espera entre reintentos es fija                                                             |
| `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`                                | `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`                                  | `chunkSize` (de 1 a 500)                                                                                     |
| `framework.browser.addAsParameter`                    | Nada que configurar: el proyecto de Playwright se envía como parámetro `project`, no forma parte de la clave |

Cada opción de Probara se describe en la
[referencia de configuración](https://docs.probara.net/es/developers/ci-integrations/playwright/configuration/#opciones). Los
ajustes de Qase sin equivalente están en [lo que no se ha portado](#lo-que-no-se-ha-portado).

### Las llamadas `qase.*`

| Qase                                                                                   | Probara                                                                                        |
| -------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- |
| `test(qase(12, 'pays'), ...)`                                                          | `test('SHOP-12 pays', ...)`, o una anotación                                                   |
| `test(qase([12, 13], 'pays'), ...)`                                                    | `test('pays', { annotation: { type: 'probara_case', description: 'SHOP-12, SHOP-13' } }, ...)` |
| Anotación `{ type: 'QaseID', description: '12' }`                                      | `{ type: 'probara_case', description: 'SHOP-12' }`                                             |
| `(Qase ID: 12)` en un título                                                           | `SHOP-12` en un título                                                                         |
| `qase.id(12)`                                                                          | `probara.id('SHOP-12')`                                                                        |
| `qase.title('...')`                                                                    | `probara.title('...')`                                                                         |
| `qase.suite('Payments\tCards')`, una anotación `QaseSuite`                             | `probara.suite(['Payments', 'Cards'])`                                                         |
| `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] })`                                                       | `await probara.attach({ name, path })`, una vez por archivo                                    |
| `qase.attach({ name, content, contentType })`                                          | `await probara.attach({ name, body, contentType })`                                            |
| `qase.step('Pay', 'Paid', 'card=visa')`                                                | `probara.step('Pay', 'Paid', 'card=visa')`                                                     |
| `qase.projects({ WEB: [3] })`, `qase.projectsTitle(...)`, una anotación `QaseProjects` | `WEB-3` como ID de caso, con `projects: ['WEB']`                                               |

Antes:

```ts
// tests/checkout.spec.ts
import { test } from '@playwright/test';
import { qase } from 'playwright-qase-reporter';

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

Después:

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

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

Lo que recibe Probara:

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

### Qué cambia 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) | Las opciones de `playwright.config`, después las variables `PROBARA_*`, 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 `playwright.config`                                          |
| IDs de caso              | Números, con un mapeo por proyecto                                                                                                     | IDs visibles (`SHOP-12`): el ID del Proyecto dirige cada ID a su proyecto                                                                                           |
| `test.fail()`            | Se ignora: un fallo esperado se envía como fallido (o inválido), y un acierto inesperado como pasado                                   | El veredicto de Playwright: un fallo esperado pasa, un acierto inesperado falla ([estados](https://docs.probara.net/es/developers/ci-integrations/playwright/results-and-retries/#estados)) |
| 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`. Nada se deduce del texto del error                                                          |
| Intentos interrumpidos   | `failed` (su documentación dice skipped)                                                                                               | `failed`                                                                                                                                                            |
| 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                                                                     |
| Llamadas a `fields()`    | La última llamada sustituye a las demás                                                                                                | Se combinan por nombre                                                                                                                                              |
| Cierre de la ejecución   | Solo con `run.complete: true`                                                                                                          | 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                                                                | `merge-reports`, o `probara run create` y `run close` ([shards](https://docs.probara.net/es/developers/ci-integrations/playwright/sharding/))                                               |
| Cuando el envío falla    | `fallback` a un informe local                                                                                                          | 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                                                                                              | Un string se envía tal cual; pasa un `Buffer` para contenido binario                                                                                                |
| Error de un resultado    | El texto del contexto de error en un campo propio                                                                                      | Todos los errores en las notas; `error-context.md` como archivo                                                                                                     |
| Intentos inestables      | `framework.markAsFlaky` marca un reintento que pasa                                                                                    | Cada intento es un resultado; sin marca ([reintentos](https://docs.probara.net/es/developers/ci-integrations/playwright/results-and-retries/#reintentos-y-pruebas-inestables))              |

### Lo que no se ha portado

| Funcionalidad de Qase                                  | Por qué                                                                                                                                           |
| ------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------- |
| Enlace público al informe (`showPublicReportLink`)     | Un enlace público a los resultados es una decisión de compartir que corresponde a la aplicación de 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; informa de los defectos desde el resultado en Probara                            |
| Perfilador de red (`profilers: ['network']`)           | Registra como pasos las llamadas HTTP del proceso de pruebas, que son ruido en pruebas de navegador; adjunta lo importante con `probara.attach()` |
| 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                                                  |
| Un campo de error aparte (`error_context`)             | Los errores van en las notas, y el contexto de error se adjunta como archivo, así que nada se recorta por el tamaño de un campo                   |
| `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          |
| `framework.markAsFlaky`                                | Cada intento es un resultado: el historial muestra la inestabilidad sin necesidad de una marca                                                    |
| Registro en un archivo (`logging.file`)                | El registro va a stderr, donde lo conserva el CI; redirige stderr para guardarlo en un archivo                                                    |

## Desde `probara import junit`

El reporter construye las mismas claves de automatización que la [CLI de Probara](https://docs.probara.net/es/developers/cli/)
asigna al reporter JUnit de Playwright (`--reporter=junit`), y lee los mismos vínculos: las
anotaciones `probara_case` (que el reporter JUnit escribe como propiedades) y los IDs de caso en los
títulos. Un proyecto que importaba el JUnit XML de Playwright con `probara import junit` puede
pasarse al reporter y conservar cada caso:

1. Mantén `rootDir` (o `testDir`) como estaba: el reporter JUnit escribe las rutas de los archivos
   de forma relativa a él.
2. Sustituye el paso de importación de JUnit por el reporter en `playwright.config`, y mantén
   `PROBARA_API_TOKEN` y `PROBARA_PROJECT` en el entorno del paso de pruebas. Guarda ahí un token de
   aplicación de la tarjeta **Playwright**, para que las nuevas ejecuciones muestren la aplicación
   Playwright.
3. Revisa las claves antes de cambiar: `probara import junit junit.xml --dry-run` imprime la clave
   de cada prueba, y un archivo de resultados escrito por el reporter con el envío desactivado
   imprime las suyas del mismo modo
   ([comprobar qué se enviaría](https://docs.probara.net/es/developers/ci-integrations/playwright/troubleshooting/#comprobar-qué-se-enviaría)).

Lo que el reporter envía además de lo que enviaba la importación de JUnit: cada intento como
resultado propio, los pasos, los adjuntos de los pasos, los metadatos y los archivos que conserva
Playwright (vídeos, trazas).

## Actualizar

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

### Política de versiones

- **0.x (ahora)**: el reporter es joven. Una versión minor (`0.2.0`) puede cambiar opciones, líneas
  del registro o valores por defecto; una versión patch (`0.1.1`) solo corrige errores. Fija la
  versión en `package.json` (o conserva el lockfile) y actualiza cuando lo decidas.
- **1.0 y posteriores**: cambios incompatibles solo en una versión major.
- El 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 major a partir de 1.0, y una minor antes:

- Una opción o una variable `PROBARA_*` eliminada o renombrada, o con un significado distinto.
- Un valor por defecto que cambia (el cierre de ejecuciones, la creación de casos que faltan, lo que
  se adjunta).
- Un helper `probara.*` eliminado, o con un comportamiento distinto.
- Un estado distinto para el mismo intento.
- **Cualquier cambio en la clave de automatización que recibe una prueba.** Una clave distinta
  desvincula la prueba de su caso: la siguiente ejecución crea uno nuevo.
- Dejar de admitir una versión de Playwright 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 incompatible. Las
líneas de stderr son para personas: no las analices con código.

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

El algoritmo es la clave de automatización 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 Playwright se convierte en las partes de la clave (el archivo relativo a `rootDir`, los
`describe` y el título, el proyecto), igual que `probara import junit` lee el JUnit XML de
Playwright ([claves de automatización](https://docs.probara.net/es/developers/ci-integrations/playwright/linking/#claves-de-automatización)).
Esa correspondencia también forma parte del contrato: una versión que cambie la clave de una prueba
es un cambio incompatible, y el changelog lo indica como tal.

Para revisar una actualización, compara lo que enviarían las dos versiones: ejecuta la suite con el
envío desactivado una vez por versión, cada una con su propio archivo de resultados
(`PROBARA_RESULTS_FILE=before.json` y después `after.json`, sin que existan antes), y compara las
dos simulaciones
([comprobar qué se enviaría](https://docs.probara.net/es/developers/ci-integrations/playwright/troubleshooting/#comprobar-qué-se-enviaría)).
Cada clave que cambie es una prueba que recibiría un caso nuevo.

### Versiones compatibles

| Requiere           | Compatible                                                                           |
| ------------------ | ------------------------------------------------------------------------------------ |
| `@playwright/test` | 1.42 y posteriores (el rango de la dependencia peer): probado en 1.42 y en la última |
| Node.js            | 22.12 y posteriores (el campo `engines`)                                             |

Algunas funciones dependen de la versión de Playwright: los archivos de los pasos y los pasos
omitidos necesitan 1.50, y el contexto de error de un intento fallido necesita 1.51
([versiones de Playwright](https://docs.probara.net/es/developers/ci-integrations/playwright/steps-and-attachments/#versiones-de-playwright)).
Nada falla con una versión anterior de Playwright dentro del rango: esos detalles simplemente no
aparecen.

### Notas de versión

- **0.1.0** (2026-09-30) es la primera versión, y la primera publicada en npm: no hay nada desde lo
  que migrar.

Siguiente: [primeros pasos](https://docs.probara.net/es/developers/ci-integrations/playwright/), o la
[referencia de configuración](https://docs.probara.net/es/developers/ci-integrations/playwright/configuration/).
