Repères et masques
Un repère est le nœud du navigateur, pas un rang, et il est vérifié de nouveau à l'instant du clic. Les masques sont dessinés sur les pixels capturés, à toute échelle d'écran et tout zoom de page.
Un repère est un nœud, pas un rang
Un rang dit « le troisième bouton ». Quand la page insère un bandeau ou une ligne, le troisième bouton est un autre, et ce peut être le bouton de paiement. Le serveur lie chaque repère au nœud que le navigateur tient pour cet élément dans cet instantané. Quand le modèle agit :
- le serveur retrouve le nœud derrière le repère et vérifie que c’est le même
nœud, toujours dans la page ; un élément déplacé ou remplacé est refusé avec
element_stale, une page qui a changé depuis l’observation avecsnapshot_stale; - il demande au navigateur quel nœud se trouve au point qu’il s’apprête à
presser, et refuse avec
element_obscuredquand ce n’est ni la cible ni l’un de ses descendants : un bandeau ou une fenêtre modale au-dessus de la cible prendrait le clic ; - après le déplacement du pointeur et avant la pression, il demande de nouveau, parce qu’un menu qui s’ouvre au survol ou un bandeau qui glisse déplace la page à ce moment-là.
À chaque refus, rien n’est cliqué, et le modèle observe de nouveau.
Mesuré sur Chromium 141 avec une page d’essai qui déplace chaque cible de 60 pixels CSS vers le bas au premier mouvement du pointeur et met un autre bouton à sa place : avant la troisième vérification, 24 clics sur 24 ne renvoyaient aucune erreur et tombaient sur le bouton mis à la place de la cible ; avec elle, 24 sur 24 sont refusés. Ce que les vérifications ne rattrapent pas est sur Limites.
Un masque est dessiné sur les pixels
Les repères et les masques sont dessinés par le serveur sur l’image capturée,
jamais dans la page. Un calque dessiné dans la page pourrait être déplacé, caché
ou lu par les scripts de la page elle-même ; un rectangle peint sur les pixels
capturés recouvre ce que le serveur a mesuré, et la valeur qui se trouve dessous
ne peut pas se retrouver depuis l’image. Si un masque ne peut pas être dessiné,
aucune image n’est publiée (capture: unavailable) ; l’arbre est servi quand
même.
Chaque boîte est en pixels CSS, si bien qu’un masque se place sur son champ quelle que soit l’échelle de l’écran. Mesuré sur Chromium 141, fenêtre de 1 280 × 800, sur le champ de mot de passe de la page de démonstration :
| Échelle | La boîte du navigateur, et celle du serveur maintenant | Celle du serveur avant la correction |
|---|---|---|
| 1,5 | 98.7, 154.1, 252.0 × 27.3 | 148.0, 231.2, 378.0 × 41.0 |
| 2 | 98.7, 154.4, 252.0 × 27.5 | 197.4, 308.9, 504.0 × 55.0 |
Aux échelles 1, 1,5 et 2, chaque pixel du champ est désormais sous son masque, et le masque ne déborde pas de plus de deux pixels.
Le zoom de la page (Ctrl et +) change la taille d’un pixel CSS ; le serveur
multiplie le découpage de l’image par le zoom que rapporte le navigateur. À 100,
125, 150 et 200 %, sur un navigateur sans interface, chaque pixel du champ est sous
son masque ; avant la correction, à 125 % et au-delà, aucun ne l’était, et
capture indiquait encore ok. L’image est désormais retenue, avec capture: unavailable, quand le navigateur ne donne aucun zoom utilisable, quand le zoom
change pendant la capture, ou quand l’image diffère de la vue de plus de deux
pixels sur l’un des deux axes.
Pour rejouer les mesures avec un navigateur sur votre PATH, depuis le dépôt :
.venv/bin/python -m pytest -q tests/test_scale_integration.py
.venv/bin/python -m pytest -q tests/test_zoom_integration.py
.venv/bin/python -m pytest -q tests/test_zoom_scrolled_integration.py
Les tableaux complets, et ce qui n’a pas été mesuré, sont dans la section 7 du guide de l’opérateur.