>_002: Reduciendo la deuda diseño-desarrollo

En el día a día nos encontramos que lo que se publica en producción no es exactamente lo diseñado en figma, o lo que tenemos en preproducción.

¿Qué solemos hacer?

Lupa, tiempo (y por consiguiente dinero) y a poner a l@s chic@s a revisar una a una las páginas o módulos para encontrar las 7 diferencias 🕵🏻

¿Qué deberíamos hacer?

Este proceso de búsqueda de diferencias deberíamos automatizarlo lo máximo posible, para ir a tiro hecho a por los que nos den posible error.

El proceso que vamos a montar es:

  • Obtenemos una captura de pantalla de la sección de la web que está online.
  • Cogemos una imagen de referencia con la que comparar.
  • Hacemos la diferencia entre ambas imágenes y obtenemos una imagen resultado con las diferencias.

Para ello tenemos disponible una librería que es la leche, Cypress.

Entre muchas otras cosas que nos permite Cypress, es visitar una url y hacer una captura de alguna de sus partes, con simplemente indicarle un selector javascript, como puede ser un id (#el-id), o una clase (.la-clase), o incluso un atributo ([atributo=valor])

cy.viewport(1280, 1162);
cy.visit('https://ablancodev.com')
cy.wait(5000)
cy.get('#intro').screenshot('modulo_intro')

Con las instrucciones de arriba le estamos definiendo un viewport, con el que indicamos las medidas (esto nos permitirá «visitar» la página por ejemplo en versión móvil o tablet)

El wait de 5 segundos es para esperar que la página «se estabilice», ya que hay algunas que entre que terminan de cargarse, cargan animaciones y demás, lo necesitan (yo por defecto lo dejo).

Y por último le decimos que obtenga el tag con id = intro, y que genere una captura de pantalla ‘modulo_intro.png’

Vale, ya tenemos la primera parte, ahora vamos a por la segunda.

Supongamos que vamos a comparar producción con un diseño que tenemos hecho. Se trataría de tener una imagen de referencia. Esta parte la vamos a hacer manual, y colocamos la imagen de referencia del módulo con las mismas dimensiones de lo que se debería generar (el viewport nos da por ejemplo el ancho)

Teniendo ya las dos imágenes a comparar, usaremos pixelmatch para hacerlo y obtener el % de diferencia.

Ya con el flujo montado, ya sólo nos falta ver el caso de uso del prototipo montado:

Web de referencia, la mía, la sección intro. Como la implementación es pixel perfect, no se encontrarían diferencias, por lo que voy a «manchar» la imagen de referencia con un par de puntos, y le he eliminado el menú, para que podamos ver las diferencias (de pixel perfect nada, lo que pasa es que no tengo figma de referencia, por lo que primero le he hecho una captura para usarla 😎):

Imagen referencia
Captura obtenida en vivo de la web

Si ejecutamos nuestro script Cypress, se nos generará una imagen con las diferencias, así como un cálculo del % de píxeles diferentes.

Obtenemos casi un 5% de diferencia de píxeles, y esta imagen de diferencia (lo rojo es lo distinto):

Así que ya no dependemos del ojo del buen cubero para encontrar las diferencias, sino que podemos encontrar las diferencias a nivel de pixel y de forma automatizada, ahorrando esfuerzos y dinero a la empresa.

El código

Cypress.Commands.add('Checksite', (url, get, cnt) => {

        //cy.viewport(1024, 1162);
        cy.visit(url)
        cy.wait(10000)
        cy.get(get).screenshot('modulo_slide_' + cnt)
        

        // PNGJS lets me load the picture from disk
        const PNG = require('pngjs').PNG;
            // pixelmatch library will handle comparison
        const pixelmatch = require('pixelmatch');


        cy.readFile(
          './cypress/screenshots/ablancodev_test1.cy.js/modulo_slide_' + cnt + '.png', 'base64'
        ).then(baseImage => {
          cy.readFile(
            './cypress/screenshotsreferencia.js/modulo_slide_' + cnt + '.png', 'base64'
          ).then(studentImage => {
            // load both pictures
            const img1 = PNG.sync.read(Buffer.from(baseImage, 'base64'));
            const img2 = PNG.sync.read(Buffer.from(studentImage, 'base64'));
        
            const { width, height } = img1;
            const { width2, height2 } = img2;

            const diff = new PNG({ width, height });
        
                    // calling pixelmatch return how many pixels are different
            const numDiffPixels = pixelmatch(img1.data, img2.data, diff.data, width, height, { threshold: 0.1 });

                    // calculating a percent diff
            const diffPercent = (numDiffPixels / (width * height) * 100);

            cy.task('log', `Found a ${diffPercent.toFixed(2)}% pixel difference`);
            cy.log(`Found a ${diffPercent.toFixed(2)}% pixel difference`);
            cy.writeFile('diff_' + cnt + '.png', PNG.sync.write(diff));

            expect(diffPercent).to.be.below(50);
          });
        });
    });
  

const pages=[
    {url: "https://www.ablancodev.com", id: 1, get: '#intro'}
]
it('Check landings',()=>{
    pages.forEach((page)=>{
        cy.Checksite(page.url, page.get, page.id);
    });
})

Otras aplicaciones

Pre vs Pro

Imagina que en vez de usar una imagen de referencia, usamos la llamada al entorno de preproducción para generar la captura de referencia. Tendríamos un comparador pre-pro 🚀

¿Nos han hackeado?

No es la primera vez que escucho que han hackeado una web, y se enteran a los pocos días cuando o bien alguien ha avisado, o alguien de la empresa se ha tropezado con el desaguisado.

Con este sistema podríamos ejecutar el script de forma periódica y detectar cuando el nivel de diferencia sea muy alto, avisando a las autoridades competentes 🚀

Bulk

Si al array final le sumamos una batería de webs/landings que tengamos, nos hemos montado un sistema de QA que supervisa todas las webs/landings de nuestra empresa, sean 1, 10, 100 o 1000. 🚀