Ilustración de Lucy sánchez

Tests de Regresión Visual para Drupal usando BackstopJS

This post is also available in english here.

Usualmente no queremos romper la funcionalidad existente. Usualmente tampoco queremos romper la apariencia de un sitio. Es ahí dónde las pruebas de regresión visual vienen al rescate: te permiten comparar 2 sets de screenshots y encontrar las diferencias entre ellos.

Hay varias herramientas para lograr esto; sin embargo, para esta guía, nos enfocaremos en BackstopJS porque es fácil de usar y de empezar a trabajar con él. Es tan sencillo como npm install backstopjs --save-dev./node_modules/.bin/backstop genConfig para obtener una configuración inicial en la cual sólo se necesitaría cambiar urls y realizar algunos ajustes menores para que sus tests funcionen.

En la configuración generada, bajo viewports se pueden agregar los diferentes tamaños de pantalla para ser probados; y bajo scenarios se tiene un objeto JSON para cada página que se desea probar. En ese objeto, se tiene la url y también podría tener referenceUrl si se quiere que el screenshot de referencia y el de test sean de urls diferentes (ej. cuando se quiere probar el sitio de producción contra el de desarrollo). Otras propiedades importantes en este objeto son onBeforeScript y `onReadyScript. Con la primera, se puede agregar javascript que será ejecutado previo a la carga de la página (por ejemplo, iniciar sesión, agregar cookies, etc) y con el segundo se puede interactuar con la página cargada como si fuera un usuario. Volveremos sobre esto más adelante para demostrar como se puede iniciar sesión en un sitio en Drupal cuando se están realizando pruebas con BackstopJS.

Entonces, dado lo anterior, se puede ver que es realmente fácil implementar esto para Drupal cuando no se necesita iniciar sesión antes de tomar los screenshots. Cuando se necesite iniciar sesión, es un poco más complejo y es lo que vamos a ver ahora.

Para nuestro ejemplo tenemos el siguiente escenario:

  • Un repositorio público con el código de Drupal dentro de una carpeta y algunas cosas extra en la raíz del repo.
  • No queremos escribir contraseñas de los usuarios existentes en el repositorio (sería muy malo para la seguridad); entonces, necesitamos iniciar sesión sin contraseña.
  • Necesitamos asegurarnos de que podamos ejecutar esto en un servicio de integración continua, de ser posible con un sólo comando.
  • Nuestro sitio en producción está hosteado en algún lado donde tenemos disponibles aliases de drush para interactuar con él.

Mientras investigaba esto, encontré este enlace que contiene una "librería" muy interesante para el manejo de cookies (DCookieManagement.js), entonces lo incluí en la carpeta casper_scripts. Además de eso, basado en ese post, creé un archivo login.js que recibe una url generada con drush uli, la abre y guarda las cookies en un archivo. Dicho archivo se ve así:

var casper = require('casper').create({
pageSettings: {
loadImages: false,//The script is much faster when this field is set to false
loadPlugins: false,
userAgent: 'My User Agent String'
}
});
var loginUrl = casper.cli.args[0];
if (typeof loginUrl === 'undefined') {
casper.exit(1);
}
// Create cookie manager object. Cookies will be saved in file called cookies.txt.
var cookiesManager = require('./DCookieManagement').create("./backstop_data/cookies/cookies.txt");
// Cookie file exists, try to read it.
if (cookiesManager.cookieFileExists()){
// If file exists, nothing to do.
casper.exit(0);
}
// First step is to open the site and instantiate cookiemanager.
casper.start().thenOpen(loginUrl, function() {
console.log("Website opened");
});
// Wait to be redirected to the Home page, and then save cookies.
casper.then(function(){
console.log("Save cookies.");
cookiesManager.loadCookies(phantom.cookies);
cookiesManager.saveCookies();
});
casper.run();

Además, agregué un archivo onBeforeLogin.js para ser ejecutado onBeforeScript e inyectar cookies cuando sea necesario:

module.exports = function (casper, scenario, vp) {
// Create cookie manager object. Cookies will be saved in file called cookies.txt.
var cookiesManager = require('./DCookieManagement').create("./backstop_data/cookies/cookies.txt");
// Cookie file exists, try to read it.
if (cookiesManager.cookieFileExists()) {
cookiesManager.readCookies();
phantom.cookies = cookiesManager.getCookies();
}
console.log('onBeforeLogin.js has run for '+ vp.name + '.');
};

Ahora, necesitamos editar el setting onBeforeScript para los escenarios que necesitan iniciar sesión y poner nuestro script onBeforeLogin.js en ellos.

Una vez hecho lo anterior, debería funcionar; sin embargo, la línea de comando para hacer que funcione es bastante compleja:

rm -f backstop_data/cookies/cookies.txt ; casperjs backstop_data/casper_scripts/login.js `drush @live.site.alias uli --name=username` ; backstop reference ; rm backstop_data/cookies/cookies.txt ; casperjs backstop_data/casper_scripts/login.js `drush @dev.site.alias uli --name=username` ; backstop test ; rm backstop_data/cookies/cookies.txt

La línea anterior realiza lo siguiente:

  • Borra el archivo de cookies en caso de que exista.
  • Ejecuta el script de casper con drush uli hacia el sitio en vivo para obtener las cookies de ese sitio.
  • Ejecuta backstop reference para obtener los screenshots de referencia.
  • Elimina el archivo de cookies.
  • Ejecuta el script de casper con drush uli hacia el sitio de desarrollo para obtener las cookies de ese sitio.
  • Ejecuta backstop test para generar los screenshots de prueba y compararlos con los de referencia.
  • Elimina el archivo de cookies.

Estaría muy bien si todo esto lo pudiéramos ejecutar con sólo uno o dos comandos. Para esto, podemos usar la sección de scripts del archivo package.json y agregar algo como esto:

"scripts": {
"backstop-reference": "rm -f backstop_data/cookies/cookies.txt ; casperjs backstop_data/casper_scripts/login.js `drush @live.site.alias uli --name=username` ; backstop reference",
"backstop-test": "rm backstop_data/cookies/cookies.txt && casperjs backstop_data/casper_scripts/login.js `drush @dev.site.alias uli --name=username` && backstop test && rm backstop_data/cookies/cookies.txt",
"backstop": "npm run backstop-reference && npm run backstop-test"
}

Ahora, si se ejecuta npm run backstop-reference se obtienen los screenshots de referencia de forma correcta incluso si alguno de ellos requiere iniciar sesión. Si se ejecuta npm run backstop-test se correrán los test incluso si algunos escenarios necesitan login. Para ejecutar ambos a la vez, se debe correr npm run backstop.

Espero que este post sea útil.


Manatí es una firma consultora web de origen costarricense, donde hacemos sitios estratégicamente diseñados para llevar organizaciones a lograr objetivos.

No se pierdan los proyectos, noticias e ideas de nosotros en Manatí. Conozca más sobre lo que hacemos, síganos en medium, twitter y facebook.