Après la fusion des modifications web par une équipe à distance, les problèmes les plus faciles à manquer ne concernent pas le chemin de Chromium, mais les comportements propres à Safari en matière de formulaires, de focus et de mise en page. Plutôt que d’ouvrir un bureau distant juste avant la mise en production pour parcourir chaque page manuellement, mieux vaut conserver sur un Mac cloud GPUMini une tâche de régression Safari WebDriver stable. Elle exécute les mêmes parcours critiques pour chaque version candidate au déploiement et conserve, en cas d’échec, l’état de la page nécessaire à une analyse ultérieure.
Définir d’abord un périmètre d’exécution reproductible
L’automatisation de Safari dépend d’une session graphique macOS et ne peut donc pas reprendre telle quelle le mode d’exécution d’un navigateur sans interface. Commencez par créer un utilisateur réservé aux tests, maintenez sa session graphique ouverte et empêchez toute exécution simultanée avec une intervention humaine à distance. Un seul processus Safari doit s’exécuter à la fois dans le même répertoire utilisateur afin d’éviter que l’historique, les fichiers téléchargés et l’état des fenêtres ne se contaminent mutuellement.
Lors de la préparation initiale du nœud, activez le pilote et lancez le diagnostic depuis un terminal interactif :
sudo safaridriver --enable
safaridriver --diagnose
mkdir -p "$HOME/browser-tests/artifacts"
L’option --enable relève de la préparation du nœud et ne doit pas être intégrée à chaque tâche automatisée. Pour les exécutions courantes, lancez uniquement le diagnostic. S’il échoue, arrêtez les tests au lieu de produire une série de délais d’attente expirés difficiles à interpréter. Les dépendances de l’environnement Python doivent elles aussi être figées : indiquez une version précise de selenium dans le fichier de dépendances du projet plutôt que d’installer la dernière version à chaque exécution.
Le premier critère d’une régression de navigateur n’est pas de « réussir à démarrer », mais d’aboutir à la même conclusion avec le même commit, les mêmes données de test et les mêmes conditions d’attente.
Isoler les tests des fluctuations externes avec des fixtures locales
Les parcours de bout en bout tels que la connexion, la recherche et le paiement peuvent utiliser l’environnement de test. En revanche, les interactions avec les composants et les contrôles de compatibilité du navigateur doivent autant que possible reposer sur des fixtures locales. Placez dans le dépôt les pages consacrées aux formulaires, aux fenêtres modales, aux téléversements et à la navigation au clavier, puis servez-les sur l’adresse de bouclage. Vous éliminerez ainsi les fluctuations liées au DNS, aux certificats et aux interfaces externes.
set -euo pipefail
ROOT="$(cd "$(dirname "$0")" && pwd)"
ARTIFACTS="$ROOT/artifacts"
mkdir -p "$ARTIFACTS"
python3 -m http.server 8080 \
--bind 127.0.0.1 \
--directory "$ROOT/fixtures" \
>"$ARTIFACTS/http-server.log" 2>&1 &
SERVER_PID=$!
cleanup() {
kill "$SERVER_PID" 2>/dev/null || true
}
trap cleanup EXIT INT TERM
"$ROOT/.venv/bin/python" "$ROOT/tests/safari_smoke.py"
Avant de réserver un port fixe, vérifiez qu’il est libre avec lsof -nP -iTCP:8080 -sTCP:LISTEN. Si plusieurs tâches partagent le même nœud, ne terminez pas des processus au hasard. Faites garantir l’exclusion mutuelle par l’ordonnanceur ou attribuez explicitement un port à chaque tâche, puis inscrivez ce port dans la configuration des tests.
Exprimer les attentes sous forme d’états métier
L’une des pratiques fragiles les plus courantes consiste à imposer une pause de quelques secondes après un clic. Une légère variation de la charge de la machine suffit alors à provoquer un faux échec si la pause est trop courte, tandis qu’une pause trop longue ralentit toute la suite. Il faut plutôt attendre un état métier observable : un bouton devenu cliquable, l’apparition d’une liste de résultats ou le passage d’un texte d’état à la valeur attendue.
from pathlib import Path
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
artifacts = Path("artifacts")
driver = webdriver.Safari()
driver.set_page_load_timeout(20)
try:
driver.get("http://127.0.0.1:8080/login.html")
wait = WebDriverWait(driver, 12)
wait.until(
EC.visibility_of_element_located((By.ID, "email"))
).send_keys("tester@local.invalid")
driver.find_element(By.ID, "continue").click()
status = wait.until(
EC.visibility_of_element_located((By.ID, "result"))
)
assert status.text == "Ready"
finally:
driver.quit()
Privilégiez des localisateurs stables comme un id ou un attribut réservé aux tests. Évitez les sélecteurs hiérarchiques susceptibles de changer au gré des ajustements de mise en page. Les assertions doivent également valider le résultat, et pas seulement la présence d’un élément : l’affichage du bouton d’envoi ne prouve pas que l’envoi a réussi, tout comme la présence du conteneur d’une liste ne signifie pas que les données ont déjà été rendues.
Rendre chaque échec reconstituable
La situation la plus coûteuse dans un test de navigateur est celle où « la CI signale un délai d’attente expiré, mais aucun élément ne permet d’examiner ce qui s’est passé ». Dès qu’un cas de test échoue, enregistrez une capture d’écran, le code source de la page, l’URL courante, le nom du cas de test et l’horodatage. Attendre la fin de toute la suite pour prendre une capture ne laisse souvent qu’une page ayant déjà changé ou une fenêtre déjà fermée.
| Symptôme | À enregistrer en premier | À vérifier en priorité |
|---|---|---|
| L’élément ne devient jamais visible | Capture d’écran, code source de la page | Calque de recouvrement, mise en page adaptative, localisateur |
| Le clic ne produit aucun résultat | URL courante, état du bouton | État désactivé, focus, liaison des événements |
| Expiration du délai de chargement | URL, journaux du service | Service local, redirection, blocage de ressources |
| Le pilote ne parvient pas à créer une session | Diagnostic du pilote | Session graphique, processus résiduels, autorisations |
Le nom du fichier de capture doit contenir l’identifiant du cas de test et un numéro d’exécution unique, mais jamais de jeton, d’adresse e-mail ou de clé de projet. Le code source de la page peut lui aussi contenir des données de test : anonymisez-le avant archivage et définissez une durée de conservation explicite.
Enregistrer immédiatement les éléments au point d’exception
Le hook d’échec du framework de test doit appeler une fonction commune de collecte des éléments de diagnostic. Comme l’enregistrement peut lui-même échouer, traitez séparément les erreurs de capture d’écran et d’écriture du code source. Une exception d’archivage ne doit jamais masquer l’assertion à l’origine de l’échec.
Récupérer les sessions résiduelles plutôt que relancer aveuglément
Lorsqu’une tâche est interrompue, Safari ou le processus du pilote peut continuer à occuper la session. Une relance immédiate lors de l’exécution suivante aboutit souvent à plusieurs échecs successifs de création de session. Vérifiez d’abord si des tâches appartenant à l’utilisateur de test sont encore actives, puis assurez-vous qu’aucun autre test légitime n’est en cours avant de mettre fin à la session résiduelle. Sur un nœud partagé, n’utilisez pas un nom de processus trop général pour effectuer un nettoyage en masse.
Les nouvelles tentatives ne conviennent qu’aux échecs transitoires et connus de création de session, avec une seule relance au maximum. Un échec d’assertion, une modification de la structure de la page ou un état métier inattendu ne doivent jamais déclencher de relance automatique, au risque de faire passer une véritable régression pour un incident occasionnel. Si une unique relance rétablit le fonctionnement, consignez-la séparément comme un événement d’instabilité au lieu de marquer la tâche comme parfaitement normale.
Enfin, séparez le contrôle en deux niveaux : exécutez les tests de fumée sur fixtures locales à chaque commit, puis lancez un petit nombre de parcours de bout en bout critiques sur les versions candidates au déploiement. Avant la mise en production, vérifiez que le diagnostic du pilote réussit, que l’utilisateur de test dispose d’un accès exclusif, que le service de fixtures est accessible, que les attentes explicites ne contiennent aucune pause fixe, que les éléments de diagnostic des échecs sont lisibles et que la fonction de nettoyage s’exécute systématiquement. Vous n’obtiendrez ainsi pas une collection de scripts de navigateur qui « réussissent parfois », mais une base de régression Safari permettant de suivre durablement les évolutions.
Questions fréquentes
Safari WebDriver peut-il fonctionner sans session graphique active ?
Ce mode ne constitue pas une base fiable. Utilisez un compte de test dédié avec une session graphique macOS ouverte et lancez le diagnostic du pilote avant les tests.
Faut-il exécuter plusieurs sessions Safari en parallèle sur un même Mac ?
Commencez par une seule session exécutée en série. Pour augmenter le débit, répartissez les groupes de tests sur des nœuds indépendants plutôt que de partager un profil.
Quels artefacts conserver après un échec Safari ?
Conservez la capture d’écran, le code source de la page, l’URL courante, le nom du test et le diagnostic du pilote. Ajoutez des journaux applicatifs préalablement expurgés.
Choisissez un Mac dans le cloud pour vos tâches de développement et de build
Comparez deux configurations M4, quatre durées de location et quatre nœuds disponibles à la vente, puis déployez selon vos besoins.