Aller au contenu

Processus de débogage, exemple sur un WooCommerce

Avatar de Raphaël Erfani
Publié le 8 septembre 2023 Par Raphaël Erfani

Découvrez comment nous avons procédé pour identifier et corriger un bug remonté par un client sur son WooCommerce. Dans notre cas, le problème est le suivant : le bouton d’ajout au panier sur les produits variables d’un WooCommerce est désactivé, impossible d’ajouter un produit variable au panier et donc de le commander. Ce bouton reste désactivé même après qu’une option ait été choisie ce qui n’est pas le fonctionnement normal de WooCommerce. L’objectif est donc de trouver pourquoi. Aucun environnement de préproduction n’était disponible donc le site a été copié sur un poste en local pour débugger.

La première chose à faire est de caractériser le problème pour savoir quoi et où chercher. Dans notre cas, les options ne sont pas affichées dans le sélecteur de couleur des produits variables. Sont-elles manquantes dès le chargement ou disparaissent-elles après ? Cela nous indique si le problème vient directement du PHP ou est causé ensuite par un appel Javascript ou une règle CSS.

source select
code source brut du select

Un affichage du code source brut puis les devtools permettent de vérifier ceci. Dans le code source brut on voit les options, le PHP les génère donc bien. C’est donc soit un souci de JS ou CSS. Dans les devtools on voit que le select apparaît une première fois puis clignote en jaune et se réaffiche vide. C’est donc un problème de Javascript car le DOM est modifié après chargement. Si c’était un souci de CSS on ne verrait pas ce clignotement. Le problème principal est qu’on ne sait pas à ce moment comment identifier quel code JS altère un nœud HTML après chargement. Il nous faut donc trouver un autre moyen de savoir où est le code responsable.

select au chargement
select au chargement avant passage du JS
select au passage du JS
select au passage du JS
select après JS
select après passage du JS

En parcourant les devtools, l’inspecteur nous indique quels événements JS sont présents sur un nœud en particulier. Ici, on vérifie notre select problématique. Un seul fichier JS est identifié ne provenant pas de WooCommerce ou de WordPress mais d’un plugin tier. Dans notre exemple, ce JS modifie l’affichage du select en color picker. Le premier test est de désactiver ce plugin tiers pour voir si le problème est toujours présent. Après désactivation, le select s’affiche mais se vide toujours. Le problème ne vient donc pas du plugin tier, il faut approfondir et voir quel autre code JS pose problème.

color picker
avec plugin activé
select vide
avec plugin désactivé

Ce code peut être dans du PHP si le JS effectue un appel AJAX. Pour vérifier cela, on utilise l’onglet Network des devtools avec uniquement l’option XHR activée. Un rechargement de la page n’affiche aucune requête dans XHR, il n’y a donc pas d’AJAX : le code cherché se trouve dans du JS.

liste requêtes XHR
liste des requêtes XHR

Pour confirmer cette hypothèse, on tente de manipuler le DOM pour que le JS n’ait plus d’effet. En supprimant les classes CSS du formulaire, le bug ne se produit plus. On a donc bien du code JS qui s’exécute au chargement et agit sur le select quand il se trouve dans ce formulaire précis. On peut aussi imaginer que ce JS utilise des données présentes quelque part dans le code, par exemple dans un data-attribute, pour effectuer ses modifications.

Comme on a désactivé le plugin de gestion des options, on sait que le bug se trouve dans WooCommerce. On tente de chercher une option dans la configuration générale et celle des variations qui pourrait indiquer de masquer les variations non commandables, mais on ne trouve rien à ce sujet. On tente également une mise à jour de WooCommerce mais le problème persiste. C’est donc que WooCommerce vérifie systématiquement des choses en JS et masque les options non disponibles.

Développeur clavier

Pour vérifier cela, on cherche dans le JS de WooCommerce un endroit faisant référence à la classe CSS identifiée précédemment. Quand celle-ci n’est pas présente dans le HTML le bug ne se produit pas. On commence par chercher le nom complet, par exemple attribute_xxx, mais sans succès. On essaie avec attribute_ au cas où le code utilise de l’interpolation, on trouve des références dans plusieurs fichiers JS dont un faisant référence aux variations. C’est une piste intéressante.

Pour confirmer cette piste on supprime le code du fichier, le bug disparaît. Le code cherché est donc bien dans ce fichier. On identifie ensuite dans le fichier une fonction qui d’après son nom, reset, pourrait être responsable. On la supprime pour confirmer, le bug disparaît. On sait maintenant où chercher !

Un nouveau problème se pose : le fichier identifié est minifié et le code de la fonction fait plusieurs dizaines de lignes. Il faudrait pouvoir travailler sur le code d’origine, mais WordPress n’a pas de build front. On a alors deux options : modifier le chargement du JS pour appeler le fichier d’origine à la place du minifié, ou travailler directement sur le minifié. Comme ça peut être compliqué avec les hooks de modifier le chargement, on choisit de travailler sur le minifié et on remplace son contenu par celui du fichier d’origine. On a ainsi accès au code lisible sans devoir faire trop de modifications qui pourraient engendrer d’autres bugs.

On commence par s’assurer que nos modifications du fichier minifié seront bien prises en compte. On y ajoute un console.log, c’est le cas. Il faut ensuite pouvoir identifier le chemin de code appelé. La fonction contient beaucoup de if/else imbriqués : on commence par mettre des console.log dans tous les chemins pour voir celui qui est suivi.

console log
identification chemin suivi avec des console.log

Une fois le chemin de code identifié on peut chercher à comprendre ce qu’il fait. Il est assez compliqué, on arrive à deviner qu’il construit un select vide, y recopie le select existant puis en supprime les options n’ayant pas la classe “attached”. Cette classe est ajoutée sous plusieurs raisons. L’une d’elles est de comparer avec === la value et le texte de l’option. En loggant les valeurs on voit que la value n’a pas de majuscule alors que l’option en a une. C’est la source de notre problème : les deux chaînes sont différentes et donc WooCommerce désactive les options.

On a :

HTML
<option value="argent">Argent</option>
<option value="blanc">Blanc</option>

WooCommerce veut :

HTML
<option value="Argent">Argent</option>
<option value="Blanc">Blanc</option>

On confirme le bug en modifiant dans WooCommerce le libellé des variations. En mettant tout en minuscule le bug disparaît. C’est étonnant que WooCommerce fonctionne ainsi alors qu’il devrait normaliser la value et l’option, par exemple avec un strtolower, avant de les comparer. Pourtant il est à jour donc ça devrait tout de même fonctionner.

Pair programming

On imagine alors plusieurs possibilités. On pourrait renommer les options, mais l’affichage se ferait sans majuscule ce qui serait moins propre. Il faudrait de plus modifier tous les produits. On pourrait ajouter le strtolower dans le code mais il faudrait modifier WooCommerce et cela casserait lors d’une future mise à jour. On se demande plutôt pourquoi ça ne fonctionne pas de base et on se rend compte que la template variation a été surchargée. Peut-être que le code généré ne correspond pas à ce que veut WooCommerce ?

Pour vérifier cela, on compare le code de la template surchagée du thème à celui de la template de WooCommerce et on trouve une différence. L’affichage de la value utilise “sanitize_title” dans le thème mais pas dans WooCommerce. On vérifie en recopiant le code de WooCommerce dans le thème, le bug disparaît.

code template surchargé
code template surchargé
code de base
code de base

Le développeur du thème a recopié la template d’origine et l’a adaptée mais WooCommerce a dû changer entre-temps et le thème n’a pas été mis à jour. Arrivé à ce stade, on a corrigé le bug : il suffit de supprimer l’appel à « sanitize_title » dans la template surchargée du thème.

On vérifie en remettant le fichier minifié et en s’assurant dans Git qu’il n’y a plus aucune modification, console.log ou autres, à part notre correctif. Les options s’affichent bien dans le select en rechargeant la page. On réactive le plugin de choix d’options et on fait plusieurs tests avec des options obligatoires ou non, le bouton d’ajout au panier fonctionne comme attendu. Le bug est corrigé !

Il reste une question en suspens : comment identifier dans les devtools quel code JS altère un nœud HTML après chargement ? Si on pouvait au moins savoir quel fichier est mis en cause, et encore mieux quelle ligne, on aurait gagné beaucoup de temps. C’est possible de le faire avec les breakpoints sur du code appelé manuellement après chargement, mais les navigateurs ne permettent pas de conserver les breakpoints après rechargement :
– Chrome : https://bugs.chromium.org/p/chromium/issues/detail?id=571519
– Firefox : https://stackoverflow.com/q/17752048
C’est tout de même possible en contournant l’utilisation :
– Sur le site en prod : https://stackoverflow.com/a/53885474
– Sur le site en dev avec possibilité de modifier le code JS : https://stackoverflow.com/a/44759558

Nous nous sommes aperçu après coup que WordPress met à disposition un système pour forcer le chargement des versions « dev » des fichiers JS et CSS (donc le code d’origine non minifié) spécifiquement pour le débug grâce la constante SCRIPT_DEBUG. La plupart des plugins utilisent cette constante pour utiliser ou non leurs fichiers JS et CSS minifiés (dont WooCommerce). Le fait de définir cette constante à true dans le fichier wp-config.php nous aurait permis de travailler directement sur le code d’origine et nous aurait donc évité de modifier les fichiers minifiés : https://fr.wordpress.org/support/article/debugging-in-wordpress/#script_debug

Corriger des bugs peut prendre du temps, et sans une approche méthodique, il est possible de passer de nombreuses heures sans trouver de solution. Les techniques de débuggage s’apprennent et un bon développeur sait comment s’y prendre. Chez Imagile, nous formons les plus jeunes à cette approche pragmatique et rationnelle.

Prêt à travailler avec nous ?

Contactez-nous, ou venez nous rencontrer pour discuter de vos projets.