Il y a peu, j'ai dû m'intéresser au sujet de la mesure d'audience d'un site et plus particulièrement valider que le plan de marquage (clics, affichage, auto-promo) soit correctement implémenté.

La régie en question ? AT-Internet[1].

Comme énoncé en introduction, je me suis plus particulièrement penché sur les trois types de marqueurs suivants :

  • Affichage : Permet de mesurer l'audience d'une page (le nombre d'affichage). Il faut faire attention car dans certains cas il est aussi nécessaire de mesurer l'audience d'une partie de page chargée en AJAX.
  • Clics : Permet de mesurer l'audience d'un bouton ou d'un lien
  • Auto-Promo : Permet de mesurer n'importe quel élément dans l'optique générale d'en mesurer l'impact. On souhaite souvent savoir si un lien, ou un encart a plus d'impact qu'un autre selon sa position sur la page.


Photo by Olloweb Solutions / Unsplash

Généralités

Fonctionnement de l'outil de monitoring d'audience

Afin de mettre en place des tests valides, il est important de comprendre le fonctionnement de l'outil et de connaitre les pré-requis au bon marquage d'une page, d'un lien ou de tout autre élément.
Pour commencer, il faut rappeler que la mesure d'audience est réalisée par le biais de requêtes vers les-dis outils. Cela peut se faire en affichant un pixel transparent sur une page ou en effectuant une requête asynchrone. Ces requêtes comportent des paramètres particuliers qui permettent d'identifier le compte, l'action ainsi que d'autres données utiles. Ce sont ces paramètres qui nous permettrons de valider le plan de marquage d'une application.

Fonctionnement de Testcafé


Photo by Robb Leahy / Unsplash

Testcafé[2] est un outil de test end-to-end utilisé pour valider une interface Web et son comportement selon le navigateur utilisé par un visiteur. Il permet de scénariser la visite d'un internaute avec des instructions simples afin de s'assurer que l'application se comporte correctement.
Cet outil permet également de surveiller les requêtes émises par l'application comme un chargement de fichier ou un appel asynchrone. C'est cette fonction qui va plus particulièrement nous intéresser car elle va nous permettre de récupérer toutes les requêtes sortantes vers l'outils Xiti.

Je ne rentrerai pas dans le détail de la configuration de l'outil car il est relativement facile à prendre en main et dispose d'une solide documentation[3]. Je mettrai cependant suffisamment de commentaires pour que chaque portion de code soit compréhensible.

Mise en place des tests

A macro shot of a Macbook Pro screen with a few dozen lines of code
Photo by Luca Bravo / Unsplash

Valider l'affichage d'une page

Pour valider l'affichage d'une page il est nécessaire de connaitre la valeurs de plusieurs variables :

  • Nom de page
  • Chapitrage : Il est facultatif et peut être découpé en trois parties (chapitre 1, chapitre 2 et chapitre 3). Généralement il s'apparente à l'arborescence du site.

Une fois ces données connues, nous pouvons écrire notre test dans le fichier load.js :

// Import required modules
import { Selector, RequestLogger } from 'testcafe';

// Set URL pattern for request logger
const xiti = /xiti.com/
// Set request logger
const logger = RequestLogger(xiti, {
    logRequestHeaders: true,
    logResponseHeaders: true,
    logResponseBody: true
});

// Declare fixture
fixture `Page Load`
    // Attach logger for all tests
    .requestHooks( logger );

test
    // Navigate
    .page( 'http://www.example.com' )
    ('Test name', async t => {
        const pageName = 'homepage';

        await t
            // Wait 5 seconds until the page is loaded (libs, images, DOMContentLoaded event)
            .wait(5000)
            // Expect that ...
            .expect(
                // At least one HTTP request ...
                logger.contains(record => {
                    var url = require( 'url' );
                    var request = new url.URL(record.request.url);

                    // Contains parameters with the correct value
                    return request.searchParams.get("p") == pageName;
                })
            )
            // Validate test or display error
            .ok('Error : Tag failed');
    });

Pour résumer rapidement ce test, nous allons afficher une page et attendre 5 secondes qu'elle soit complètement chargée. Pendant son chargement nous allons capturer toutes les requêtes vers le domaine xiti.com. Nous allons ensuite parcourir toutes les requêtes vers xiti.com afin de vérifier qu'au moins l'une d'entre elle comporte le paramètre p avec la bonne valeur.

Pour lancer le test nous exécutons la commande suivante :

$> docker run \
    --rm \
    --name testcafe \
    -a stdout \
    -a stderr \
    -v $(pwd):/tests \
    testcafe/testcafe \
    firefox /tests/load.js
    
 Running tests in:
 - Firefox 57.0.0 / Linux 0.0.0

 Page Load
 ✓ Test name

Valider un clic de navigation

Globalement, il ne va pas se passer grand chose de plus que sur l'affichage d'une page. La seule différence consiste à intéragir avec la page, à savoir cliquer sur un élément.
Les pages d'un site étant relativement vivantes de nos jour, il n'est pas rare de devoir naviguer un peu avant d'atteindre l'élément que l'on souhaite tester si celui-ci n'est pas visible par défaut.

Il y a également des paramètres différents à valider :

  • Chapitrage
  • Libellé du clic
  • Type de clic : exit, download, action ou navigation
  • Identifiant

Nous rédigeons notre test dans le fichier click.js :

// Import required modules
import { Selector, RequestLogger } from 'testcafe';
// Set URL pattern for request logger
const xiti = /xiti.com/
// Set request logger
const logger = RequestLogger(xiti, {
    logRequestHeaders: true,
    logResponseHeaders: true,
    logResponseBody: true
});
// Click type object
const clickType = {
    exit: 'S',
    download: 'T',
    navigation: 'N',
    action: 'A'
};

// Declare fixture
fixture `Click`
    // Attach logger
    .requestHooks( logger );

test
    // Navigate
    .page( 'http://www.example.com' )
    ('Click submenu1', async t => {
        const type = 'navigation',
            chapter1 = 'myChapter1',
            chapter3 = 'myChapter3',
            label = 'monitoredLink',
            marker = chapter1 + "::" + chapter3 + "::" + label,
            targets = [
                '#menu a.submenu1',
                '#submenu1 a.out'
                ];

        // Wait 5 seconds until the page is loaded (libs, images, DOMContentLoaded event)
        await t.wait(5000);
        
        // Clear logged requests
        logger.clear();
        
        // Navigate to the final target
        for (let i = 0; i < targets.length; i++) {
            await t.click( targets[i] );    
        }
        
        // Test click tag
        await t
             // Expect that ...
            .expect(
                // At least one HTTP request ...
                logger.contains(record => {
                    var url = require( 'url' );
                    var request = new url.URL(record.request.url);

                    // Contains parameters with the correct value
                    return request.searchParams.get("p") == marker &&
                        request.searchParams.get("click") == clickType[type];
                })
            )
            // Validate test or display error
            .ok('Errorr : Tag failed');
    });

Il ne reste plus qu'à valider le test

Comme dans l'exemple précédent, nous allons afficher la page et attendre 5 secondes qu'elle soit complètement chargée. Nous nettoyons ensuite l'ensemble des requêtes capturées afin de ne pas polluer notre monitoring avec des données qui pourraient être fausses.
Afin d'accéder à l'élément à tester, un sous-menu fictif non affiché par défaut, nous simulons les actions d'un utilisateur pour le rendre visible. Ensuite, comme pour le cas précédent, nous allons parcourir toutes les requêtes vers xiti.com afin de vérifier qu'au moins l'une d'entre elle comporte les bonnes valeurs de paramètre (ici nous allons tester les paramètres p et click).

Nous lançons ce nouveau test avec la commande suivante :

$> docker run \
    --rm \
    --name testcafe \
    -a stdout \
    -a stderr \
    -v $(pwd):/tests \
    testcafe/testcafe \
    firefox /tests/click.js
    
 Running tests in:
 - Firefox 57.0.0 / Linux 0.0.0

 Click
 ✓ Click submenu1

Valider une auto-promotion

Avant de rentrer dans le détail de l'implémentation il faut savoir qu'une auto-promotion n'est ni plus ni moins que la combinaison des deux types de marqueurs précédents à la différence qu'on ne teste pas les mêmes paramètres.
En effet, une auto-promotion consiste à effectuer au moins un des événements suivants :

  • Afficher un élément promu
  • Cliquer sur un élément promu

Il y aura donc deux événements à surveiller. Le premier au chargement de la page et le second, s'il est activé, au clic sur un élément promu.

Nous rédigeons notre test dans le fichier self-promoted.js :

// Import required modules
import { Selector, RequestLogger } from 'testcafe';

// Set URL pattern for request logger
const xiti = /xiti.com/
// Set request logger
const logger = RequestLogger(xiti, {
    logRequestHeaders: true,
    logResponseHeaders: true,
    logResponseBody: true
});

// Declare fixture
fixture `Click`
    // Attach logger
    .requestHooks( logger );
    // Navigate
    .page( 'http://www.example.com' )

test
    ('Web callback', async t => {
        const id = 11,
            chapter1 = 'myChapter1',
            chapter2 = 'myChapter2',
            chapter3 = 'myChapter3',
            label = 'monitoredLink',
            marker = 'INT-' + id + '-[' + chapter1 + '::' + chapter2 + '::' + chapter3 + '::' + label + ']',
            target = '#callBack';

        // Test print tag
        await t
            // Wait 5 seconds until the page is loaded (libs, images, DOMContentLoaded event)
            .wait(5000)
            // Expect that ...
            .expect(
                // At least one HTTP request ...
                logger.contains(record => {
                    var url = require( 'url' );
                    var request = new url.URL(record.request.url);

                    // Contains parameters with the correct value (sort of :) )
                    return request.searchParams.get("ati").indexOf(marker) != -1;
                })
            )
            .ok('Error : Print tag failed');

        // Clear logged requests
        logger.clear();

        // Test click tag
        await t.click( target )
            // Expect that ...
            .expect(
                // At least one HTTP request ...
                logger.contains(record => {
                    var url = require( 'url' );
                    var request = new url.URL(record.request.url);

                    // Contains parameters with the correct value
                    return request.searchParams.get("atc") && 
                        request.searchParams.get("atc").indexOf(marker) != -1;
                })
            )
            // Validate test or display error
            .ok('Error : Click tag failed');
    });

Nous allons donc dans un premier temps valider, après le chargement de la page qu'un tag d'impression soit envoyé puis nous allons simuler une action utilisateur afin de valider par la suite que le tag de clic soit bien envoyé à son tour.
Il faut noter que les paramètres à tester ne sont plus les mêmes car il dépendent du type de tag mis en place.

Nous lançons maintenant le nouveau test avec la commande suivante :

$> docker run \
    --rm \
    --name testcafe \
    -a stdout \
    -a stderr \
    -v $(pwd):/tests \
    testcafe/testcafe \
    firefox /tests/self-promoted.js
    
 Running tests in:
 - Firefox 57.0.0 / Linux 0.0.0

 Click
 ✓ Web callback

Conclusion

Avec quelques outils et très peu de configuration, il est possible de valider la mise en place d'une solution de suivi d'audience telle que Xiti. Il est à mon avis tout à fait possible de réaliser la même chose avec Google Analytics.


  1. AT Internet (Applied Technologies Internet) est une entreprise française créée en 1996 spécialisée dans la mesure d'audience et de performance de sites web, mobiles, applications et réseaux sociaux. AT Internet est l’éditeur historique de l’outil de mesure XiTi, connu dans les années 2000. (Wikipedia) ↩︎

  2. Testcafé ↩︎

  3. Documentation Testcafé ↩︎