Decouvrir Tenon.io

publié le

L’accessibilité est un facteur important du Web, en effet celui-ci peut-être utilisé par presque l’intégralité des personnes possédant un handicap si et seulement si les développeurs font l’effort minimal de rendre leurs sites et leurs applications accessibles. Un bon niveau d’accessibilité est : utilisable par tous les handicaps visuels, ce qui inclue le daltonisme (couleurs), la myopie aiguë (gérer le redimensionnement des polices), la cécité (utilisation par une synthèse vocale), …

En général un site accessible à un aveugle intègre suffisamment de bonnes pratiques pour être défini comme Accessible. En 2016 est sorti Tenon.io un service SaaS facilitant le contrôle de l’accessibilité de son site, il fonctionne par url, en local sur le poste de développement et on peut aisément le brancher sur une intégration continue. Je vous propose une introduction à la mise en place de Tenon.io sur un projet web, en prenant un exemple => mon site.

Premiers pas

La première chose à faire pour se familiariser avec l’outil, c’est de le lancer sur une page web statique au hasard https://www.cedriclegallo.fr : résultat de test sur Tenon.io

Plutôt mauvais, 10 erreurs sur la homepage, et je suis en train de vous expliquer qu’il faut rendre votre site accessible. Le pire c’est qu’il s’agit de mauvaises pratiques ultra-basiques et valable pour le référencement également (alt sur les images et texte dans les liens même quand il s’agit de boutons graphiques, tous les deux indispensables à un aveugle) mais je suis faillible, mettre en production rapidement conduit à oublier certaines vérifications élémentaires qui sont vite fastidieuses si réalisées manuellement. C’est tout l’intérêt d’un service comme Tenon.io, vous le branchez en intégration continue au démarrage de chacun de vos projets, et vous avez en temps réel les nouveaux rapports. Bien branchons la vérification en local sur mon PC de développement, afin de corriger au plus vite ces erreurs.

Vérification locale

Pour démarrer en local vous avez plusieurs possibilités en fonction de votre workflow : module node, plugin gulp, plugin grunt, plugins pour les navigateurs chrome, opera et firefox, module php, module python, monule selenium, … Il vous faut au préalable vous enregistrer au service Tenon.io afin d’avoir votre API Key. On démarre donc en suivant le quickstart pour Node.js en se plaçant dans un répertoire créé pour l’occasion :

npm init

Amorcez un fichier test.js comme indiqué dans la documentation de tenon-node en remplaçant les paramètres API_KEY et l’url à tester par votre code html (car Tenon.io n’aura pas acces à localhost) :

'use strict'

const querystring = require('querystring')
const http = require('http')
const https = require('https')

http.get('http://localhost:8000/pages/home/', (res) => {
    res.setEncoding('utf8')
    let rawData = ''
    res.on('data', (chunk) => rawData += chunk)
    res.on('end', () => {
        try {
            var data = querystring.stringify({
                src: rawData,
                key: 'API_KEY'
            })

            const options = {
                host: 'tenon.io',
                port: 443,
                path: '/api/',
                method: 'POST',
                headers: {
                    'Content-Type': 'application/x-www-form-urlencoded',
                    'Content-Length': Buffer.byteLength(data)
                }
            }

            const req = https.request(options, res => {
                res.setEncoding('utf8')
                res.on('data', function (chunk) {
                    console.log("body: " + chunk)
                })
            })

            req.write(data)
            req.end()
        } catch (e) {
            console.log(e.message)
        }
    })
}).on('error', (e) => {
    console.log(`Got error: ${e.message}`)
})

ensuite lancez le premier test :

node test.js

On retrouve bien nos 10 erreurs dans un JSON, et également stockée sur Tenon.io dans la rubrique History. Il est grand temps de corriger ces erreurs et de tester l’intégralité du site.

Tester l’intégralité du site

L’API Tenon.io permet de tester les pages uniquement 1 par 1, il n’y a pas de requêtes par liste qui décompterait en lot. Il nous faut donc appeler individuellement pour chaque page de notre site l’API. Chaque page de notre site c’est une information stockée dans sitemap.xml. On écrit donc un petit script qui parse sitemap.xml et qui exécute les requêtes correspondantes sur Tenon.io (toujours en localhost). Ce dont on a besoin c’est la liste des URL, en fonction de comment est écrit votre sitemap, vous aurez besoin de parser/transformer le résultat, ou bien comme dans mon cas faire de la simple transformation de chaînes de caractères.

'use strict'

const querystring = require('querystring')
const rp = require('request-promise-native');

rp('http://localhost:8000/sitemap.xml').then( (res) => {
    const promises = res.split('\n')
    //supprimer en-tete et footer xml pour avoir les urls uniquement
    .filter(_.indexOf('https://www.cedriclegallo.fr') !== -1)
    //retrait de <url> <loc> et de </loc> </url> et mise de l'url en localhost
    .map(_ => _.substring(11, _.length - 13).replace('https://www.cedriclegallo.fr', 'http://localhost:8000'))
    .map(_ => analyse(_))//conversion en promesses
    Promise.all(promises).then(_ => handleResults(_))
})

Après il suffit de rejouer le code du chapitre précédent sur ce tableau d’URLs (la fonction analyse). Lancer N requêtes asynchrones en parrallèles ressemble beaucoup à ce qu’on attend d’un Promise.all, le module http natif de node implémente le Writable Stream et comme chacun le sait les Promise et les Stream ce n’est pas pareil. Plusieurs options s’ouvrent à nous en fonction de ce qui vous inspire le plus : Transformer les streams en promesses (avec une lib du genre stream-to-promise), le faire à la main, utiliser une lib de requêtes gérant les promise (comme request-promise-native), utiliser la programmation Reactive RxJS, … J’ai choisi l’option request-promise-native pour aller plus vite mais l’idée programmation réactive est assez séduisante.

function analyse(url){
    return rp(url).then( response => {
        const data = querystring.stringify({
            src: response,
            key: 'API_KEY'
        })

        const options = {
            uri: 'https://tenon.io/api/',
            method: 'POST',
            headers: {
                'Content-Type': 'application/x-www-form-urlencoded',
                'Content-Length': Buffer.byteLength(data)
            },
            body: data
        }

        return rp(options).promise()
    })
}

Résultats : 9 erreurs et 4 warnings malgré les corrections sur la page d’accueil. Cela valait le coup de tester.

Il ne reste plus qu’à faire quelque chose avec notre JSON de sortie pour mettre en forme la sortie console (par exemple écrire dans un fichier ou traiter dans une interface), et on aura le travail fin prêt pour l’automatisation, on en profite pour corriger les dernières erreurs.

const fs = require('fs')

function handleResults(data){
    fs.writeFile('results.json', data, (err) => {
        if (err) throw err;
        console.log('It\'s saved!');
    });
}

Intégration continue

Je pars du principe que l’intégration continue a accès à Tenon.io donc au web, à partir de là 2 possibilités:

  • soit la plate-forme n’est pas accessible depuis internet mais l’est depuis la plate-forme d’intégration continue auquel cas modifiez la ligne du replace du script local pour qu’il fonctionne avec l’environnement cible
  • soit le code est déployé sur une plate-forme de pré-production ou production accessible depuis le web, auquel cas appelez Tenon.io directement avec les URLs du sitemap (suppression de la partie replace et appel de la fonction analyseURL ci-dessous)
function analyseURL(url){
    const data = querystring.stringify({
        url: url,
        key: 'API_KEY'
    })

    const options = {
        uri: 'https://tenon.io/api/',
        method: 'POST',
        headers: {
            'Content-Type': 'application/x-www-form-urlencoded',
            'Content-Length': Buffer.byteLength(data)
        },
        body: data
    }

    return rp(options).promise()
}

A noter que Tenon.io recommande de tester avec les urls plutot qu’avec le code source pour un résultat le plus proche possible de la cible.

Pricing

Et oui ce service si génial n’est pas gratuit, si le trial de 30jours a été largement suffisant pour écrire cet article, l’utilisation de l’API de manière pérenne nécessite un effort financier. Il vous en coutera 9$ par mois pour 250 appels en 1 mois, au-delà le prix augmente, cependant Tenon.io propose des réductions sur demande pour l’open source, les gouvernements, les écoles publiques, les associations et les bootcamp de développeurs web.

Du coup afin de ne pas envoyer en l’air votre quota, je vous invite à vérifier uniquement l’accessibilité des pages ayant été modifiées et à chaque changelog de Tenon.io (inclusion de nouvelles règles).

Pertinence du service

Tenon.io est encore bien jeune, et la définition d’un site accessible est celle du W3C-WCAG 2.0, et il est impossible de l’automatiser intégralement, cette page vous indique quelle couverture l’outil a du standard. A vous de voir si automatiser cette partie vaut le prix que réclame le service. Un bon moyen de comparaison est de voir le delta avec l’audit d’accessibilité de chrome dev tool (gratuit, mais nécessitant de tester chaque page manuellement). Mais souvenez-vous que rien ne remplace le test final en situation de handicap.

Conclusion

Cela faisait un petit moment que je souhaitais tester Tenon.io pour voir à quel point il était facile de l’intégrer sur un projet web. Dans le cas d’une SPA avec authentification, le module Selenium fera le travail moyennant l’écriture des scénarios indiquant les pages à envoyer à l’API. Pour les simples sites, c’est vraiment trivial en utilisant simplement l’API native, il y a des modules pour simplifier encore plus. Le coût d’intégration de Tenon.io sur vos projets est totalement transparent, et il fouille bien dans les recoins de votre site les défaut d’accessibilité. Moralité, si l’accessibilité est un enjeu pour votre projet, mettre en place Tenon.io dès le départ peut-être une forte plus-value, le tarif n’est pas prohibitif, mais suffisament dissuasif pour vous empêcher de l’utiliser sur des projets gratuits et c’est dommage. Je ne connais pas la nature des réductions pour les projets Open Source, mais ce serait cool s’ils avaient une offre full gratuite pour les projets Open Source à usage non commercial afin de rendre le web plus accessible et donc meilleur.