Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Send the reports generated after the tests in email using Github actions #30766

Open
GbrlSouza opened this issue Dec 16, 2024 · 2 comments
Open
Labels
stage: needs information Not enough info to reproduce the issue

Comments

@GbrlSouza
Copy link

GbrlSouza commented Dec 16, 2024

What would you like?

Hello, good morning... what's up?

I'm Gabriel Souza, Developer and QA Analyst at GRV Software, Vinhedo - São Paulo - Brazil.

I recently added a feature that was not included in the Cypress documentation. The idea is to automatically send the reports generated after the tests in email using Github actions.

  1. In the root of the test there will be a file called sendReport.js which contains all the sending information with the reports generated after the tests.
  • These files will have a limit of 25MB, which is the maximum allowed per attachment.
require('dotenv').config()

const nodemailer = require('nodemailer')
const path = require('path')
const fs = require('fs')
const { create } = require('html-pdf-chrome')
const puppeteer = require('puppeteer')

const MAX_ATTACHMENT_SIZE = 24 * 1024 * 1024

async function ensureDirectoryExists(directoryPath) {
  try {
    await fs.promises.mkdir(directoryPath, { recursive: true })
    console.warn(`Diretório criado ou já existe: ${directoryPath}`)
  } catch (error) {
    console.error('Erro ao criar diretório:', error)
  }
}

async function captureScreenshot(url, outputPath) {
  try {
    const browser = await puppeteer.launch({
      headless: true,
      executablePath: '/usr/bin/chromium-browser',
    })
    const page = await browser.newPage()

    await page.goto(`file://${url}`, { waitUntil: 'networkidle2' })
    await page.screenshot({ path: outputPath, fullPage: true })

    await browser.close()
    console.debug('Screenshot capturada com sucesso:', outputPath)
  } catch (error) {
    console.error('Erro ao capturar screenshot:', error)
  }
}

function findVideoFileDynamically(videoDir) {
  try {
    if (!fs.existsSync(videoDir)) {
      console.warn('Diretório de vídeos não existe:', videoDir)
      return null
    }

    const directories = fs.readdirSync(videoDir, { withFileTypes: true })
    for (const dir of directories) {
      if (dir.isDirectory()) {
        const subDirPath = path.join(videoDir, dir.name)
        const files = fs.readdirSync(subDirPath)
        const videoFile = files.find(file => file.endsWith('.mp4'))
        if (videoFile) {
          console.debug(`Vídeo encontrado: ${videoFile} em ${subDirPath}`)
          return path.join(subDirPath, videoFile)
        }
      }
    }
    console.warn('Nenhum arquivo de vídeo encontrado nos subdiretórios:', videoDir)
    return null
  } catch (error) {
    console.error('Erro ao procurar o arquivo de vídeo nos subdiretórios:', error)
    return null
  }
}

async function sendEmailWithAttachment(pdfPath, screenshotPath, videoPath) {
  const attachments = [
    {
      filename: 'relatorio-cypress.pdf',
      path: pdfPath,
    },
    {
      filename: 'screenshot.png',
      path: screenshotPath,
    },
  ]

  if (videoPath && fs.existsSync(videoPath)) {
    const videoStats = await fs.promises.stat(videoPath)
    const totalSize = await calculateTotalAttachmentSize(attachments)

    if (totalSize + videoStats.size <= MAX_ATTACHMENT_SIZE) {
      attachments.push({
        filename: 'video.mp4',
        path: videoPath,
      })
    } else {
      console.warn('O tamanho total dos anexos excede 20MB, vídeo não será anexado.')
    }
  } else {
    console.warn(`Arquivo de vídeo não encontrado ou inválido: ${videoPath}`)
  }

  const mailOptions = {
    from: process.env.EMAIL,
    to: process.env.RECIPIENT,
    subject: 'Relatório de Testes Cypress',
    html: '<h3>Segue o relatório de testes do Cypress</h3>',
    attachments,
  }

  try {
    const info = await transporter.sendMail(mailOptions)
    console.debug('E-mail enviado com sucesso:', info.response)
  } catch (error) {
    console.error('Erro ao enviar o e-mail:', error)
  }
}

async function convertHtmlToPdf(htmlFilePath, outputPdfPath) {
  try {
    if (!fs.existsSync(htmlFilePath)) {
      console.error('Erro: Arquivo HTML não encontrado:', htmlFilePath)
      return
    }

    const htmlContent = await fs.promises.readFile(htmlFilePath, 'utf-8')

    const options = {
      launchOptions: {
        headless: true,
        executablePath: '/usr/bin/google-chrome',
      },

      printOptions: {
        format: 'A4',
        landscape: true,
        printBackground: true,
        preferCSSPageSize: true,
      },
    }

    const pdf = await create(htmlContent, options)
    await new Promise(resolve => setTimeout(resolve, 5000))
    await pdf.toFile(outputPdfPath)
    console.debug('Relatório convertido para PDF com sucesso:', outputPdfPath)
  } catch (error) {
    console.error('Erro ao gerar o PDF:', error)
  }
}

async function calculateTotalAttachmentSize(attachments) {
  let totalSize = 0

  for (const attachment of attachments) {
    try {
      const stats = await fs.promises.stat(attachment.path)
      totalSize += stats.size
    } catch (error) {
      console.error('Erro ao calcular o tamanho do anexo:', error)
    }
  }

  return totalSize
}

const transporter = nodemailer.createTransport({
  host: 'smtp.gmail.com',
  port: 465,
  secure: true,
  auth: {
    user: process.env.EMAIL,
    pass: process.env.PASSWORD,
  },
})

const reportsDir = path.resolve(__dirname, 'cypress/reports')
const reportPath = path.join(reportsDir, 'html', 'index.html')
const pdfDir = path.join(reportsDir, 'pdf')
const pdfPath = path.join(pdfDir, 'relatorio-cypress.pdf')
const videoDir = path.join('cypress/videos')
const screenshotPath = path.join(pdfDir, 'relatorio-cypress.png')

ensureDirectoryExists(pdfDir).then(async () => {
  try {
    await convertHtmlToPdf(reportPath, pdfPath)
    await captureScreenshot(reportPath, screenshotPath)

    const videoPath = findVideoFileDynamically(videoDir)

    if (!fs.existsSync(pdfPath)) {
      console.error('Erro: O arquivo PDF não foi gerado corretamente.')
      return
    }

    await sendEmailWithAttachment(pdfPath, screenshotPath, videoPath)
  } catch (error) {
    console.error('Erro durante o processamento:', error)
  }
})
  • There will be a .env file that stores the sender and recipient email addresses with the security key.

  1. in the path ".github/workflows/ci.yml", in addition to running the tests, it will also be responsible for sending the email if there are errors in the tests.
name: Cypress Tests

on: 
  push:
    branches:
      - main

jobs:
  cypress-run:
    runs-on: ubuntu-22.04
    steps:
    - name: Checkout Repository
      uses: actions/checkout@v4

    - name: Install Dependencies
      run: npm install

    - name: Install Cypress
      run: npm install cypress --save-dev

    - name: Verify Cypress Installation
      run: npx cypress verify

    - name: Fix Cypress Permissions
      run: chmod +x ./node_modules/.bin/cypress

    - name: Run Cypress Tests
      run: npx cypress run

    - name: Upload Cypress Artifacts
      uses: actions/upload-artifact@v3
      with:
        name: cypress-artifacts
        path: |
          cypress/videos/*
          cypress/screenshots/*
          cypress/reports/pdf/*

  send-report-test:
    if: failure()

    runs-on: ubuntu-22.04
    needs: cypress-run
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Run Send Report (with email)
        run: |
          echo "Testes falharam. Enviando relatório..."
          node sendReport.js
          echo "Enviando e-mail de erro..."

      - name: Send Email Notification
        run: echo "Os testes Cypress falharam. Relatórios e vídeos estão anexados."

  1. The files generated by the tests will be made available in the Github actions.

image


Why is this needed?

This is necessary to reduce the manual work of converting html to pdf, capturing screenshots, and recording video with third-party software, and also so that the recipients (whether a supervisor or a manager in charge of the department) are aware of possible errors that the tests have captured.

Other

If you have any questions about this idea, check out my repost https://github.com/GbrlSouza/cypress_with_email

@jennifer-shehane
Copy link
Member

@GbrlSouza Hi, is this a request for this feature in Cypress?

@jennifer-shehane jennifer-shehane added the stage: needs information Not enough info to reproduce the issue label Dec 20, 2024
@jennifer-shehane jennifer-shehane changed the title Send Report Send the reports generated after the tests in email using Github actions Dec 20, 2024
@GbrlSouza
Copy link
Author

Hi @jennifer-shehane, what's up?
That's what it would be for too ... but it's also an idea of what can be added to the documentation as an option to send a personalized email with all the information from the test run in the github actions.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
stage: needs information Not enough info to reproduce the issue
Projects
None yet
Development

No branches or pull requests

2 participants