DEV Community

Cover image for Cómo logré adjuntar videos comprimidos de Cypress en Allure Report
Juan Martin Ruiz
Juan Martin Ruiz

Posted on

Cómo logré adjuntar videos comprimidos de Cypress en Allure Report

Cuando empecé a integrar Cypress con Allure Reports, me encontré con una limitación inesperada: los videos de ejecución no se adjuntaban correctamente al reporte.

La situación era frustrante porque Cypress sí generaba los videos, pero Allure lo hacía con los archivos sin comprimir, lo que aumentaba mucho el peso de los reportes.

npx cypress run
npx allure generate allure-results --clean
npx allure open
Enter fullscreen mode Exit fullscreen mode

En este post quiero contar el problema, el análisis y la solución que implementé, por si le sirve a alguien más que esté atravesando lo mismo.

El problema

Por defecto, Cypress genera un video por cada spec ejecutado y lo comprime una vez finalizado el run. Si tenemos activado con video: true y videoCompression: true en cypress.config.js
Sin embargo, cuando usamos @shelex/cypress-allure-plugin, los archivos que quedaban en allure-results eran los videos sin comprimir.

La consecuencia:

  • Reportes con videos pesados.

El análisis

Revisando la configuración encontré dos puntos clave:

  1. Cypress primero genera un video “crudo” y después lo reemplaza por uno comprimido. El problema es que Allure a veces copiaba el video crudo antes de la compresión.
  2. Dependiendo de cómo configures after:spec y after:run, podés controlar si se eliminan videos de tests pasados o si se reemplazan manualmente los archivos que Allure guarda.

La solución

Decidí crear un script en la carpeta util que se ejecuta en el evento after:run y reemplaza en allure-results los videos por las versiones comprimidas que Cypress deja en cypress/videos.

Ejemplo:

// cypress.config.js
const { defineConfig } = require("cypress");
const allureWriter = require('@shelex/cypress-allure-plugin/writer');

const { attachCompressedVideosToAllure } = require('./cypress/utils/attachCompressedVideosToAllure');

module.exports = defineConfig({
  e2e: {
    setupNodeEvents(on, config) {
      allureWriter(on, config);

      on('after:run', (runResults) => attachCompressedVideosToAllure(runResults, { resultsDir: 'allure-results' }));

      return config;
    },
  },
  env: {
    allure: true,
    allureAddVideoOnPass: false
  },
  video: true,  
  videosFolder: 'cypress/videos',
  videoCompression: 32,
  screenshotOnRunFailure: false
});
Enter fullscreen mode Exit fullscreen mode
//cypress\utils\attachCompressedVideosToAllure.js
const { promises: fsp } = require('fs');
const path = require('path');

async function attachCompressedVideosToAllure(runResults, { resultsDir = 'allure-results' } = {}) {
  if (!runResults?.runs?.length) return;
  const resultsAbs = path.resolve(resultsDir);

  let jsonFiles = [];
  try {
    jsonFiles = (await fsp.readdir(resultsAbs)).filter(f => f.endsWith('.json'));
  } catch { return; }

  const index = (await Promise.all(jsonFiles.map(async (f) => {
    try { return { file: f, fullPath: path.join(resultsAbs, f), data: JSON.parse(await fsp.readFile(path.join(resultsAbs, f), 'utf8')) }; }
    catch { return null; }
  }))).filter(Boolean);

  await Promise.all(runResults.runs.map(async (r) => {
    const video = r?.video;
    if (!video) return;
    try {
      const specName = path.basename(r.spec?.relative || r.spec?.name || video);
      const specKey  = (r.spec?.relative || specName).replace(/[\/\\]/g, '__');
      const target   = `video__${specKey}.mp4`;
      await fsp.copyFile(video, path.join(resultsAbs, target));

      const related = index.filter(it => JSON.stringify(it.data?.labels || []).includes(specName));
      await Promise.all(related.map(async (it) => {
        let touched = false;
        for (const att of (it.data.attachments || [])) {
          if (att?.type === 'video/mp4') { att.source = target; touched = true; }
        }
        if (touched) await fsp.writeFile(it.fullPath, JSON.stringify(it.data), 'utf8');
      }));
    } catch (e) {
      console.error('[after:run]', e?.message);
    }
  }));
}

module.exports = { attachCompressedVideosToAllure };
Enter fullscreen mode Exit fullscreen mode
npx cypress run
npx allure generate allure-results --clean
npx allure open
Enter fullscreen mode Exit fullscreen mode

De adjuntar videos 1.2 MB pasamos a adjuntar videos 300 KB

Eliminar videos de pruebas exitosas (opcional)

Si querés que los videos de pruebas exitosas se eliminen automáticamente para mantener el reporte más limpio, podés agregar un script dentro del evento after:spec

//cypress\utils\deleteVideoIfSpecPassed.js
const { promises: fsp } = require('fs');

async function deleteVideoIfSpecPassed(_spec, results) {
  if (!results?.video) return;
  const hasFailures =
    (results.tests || []).some(t => (t.attempts || []).some(a => a.state === 'failed')) ||
    (results.stats?.failures > 0);
  if (!hasFailures) {
    try { await fsp.unlink(results.video); }
    catch (e) { console.warn('[after:spec] No se pudo borrar el video:', e?.message); }
  }
}

module.exports = { deleteVideoIfSpecPassed };
Enter fullscreen mode Exit fullscreen mode

Llamamos a deleteVideoIfSpecPassed dentro del evento after:spec

//cypress.config.js
const { defineConfig } = require("cypress");
const allureWriter = require('@shelex/cypress-allure-plugin/writer');

const { deleteVideoIfSpecPassed } = require('./cypress/utils/deleteVideoIfSpecPassed');
const { attachCompressedVideosToAllure } = require('./cypress/utils/attachCompressedVideosToAllure');

module.exports = defineConfig({
  e2e: {
    setupNodeEvents(on, config) {
      allureWriter(on, config);

      on('after:spec', deleteVideoIfSpecPassed);
      on('after:run', (runResults) => attachCompressedVideosToAllure(runResults, { resultsDir: 'allure-results' }));

      return config;
    },
  },
  env: {
    allure: true,
    allureAddVideoOnPass: false
  },
  video: true,  
  videosFolder: 'cypress/videos',
  videoCompression: 32,
  screenshotOnRunFailure: false
});
Enter fullscreen mode Exit fullscreen mode

Resultado

  • Las versiones comprimidas de Cypress ahora se adjuntan correctamente en Allure Report, lo que hace que los reportes sean más livianos y fáciles de compartir.
  • Los videos de pruebas exitosas se eliminan automáticamente para mantener el reporte más limpio.

Reflexión

Muchas veces, cuando integramos herramientas, aparecen estos “detalles” que no están documentados en ningún lado.

Resolverlo me obligó a entender mejor cómo Cypress maneja los videos y cómo Allure gestiona los attachments.

Ahora que lo tengo funcionando, quise compartirlo porque seguramente más de uno se va a encontrar con el mismo dolor de cabeza.

Si querés ver el código completo y en contexto, lo publiqué en este repositorio: https://github.com/jmr85/cypress-video-allure-reports

Top comments (0)