Tutorials

Shadow DOM et CAPTCHA : accès aux éléments dans les Web Components

Périmètre sûr : ce guide s'applique à vos propres applications (développement, QA, préproduction, production) ou à des systèmes pour lesquels vous disposez d'une autorisation écrite. Il ne couvre ni l'automatisation de sites tiers ni l'anti-détection.

Un CAPTCHA encapsulé dans un Web Component reste accessible depuis vos tests : interrogez le shadowRoot du composant hôte au lieu de document. C'est la seule vraie différence. CaptchaAI renvoie le token comme pour un formulaire classique ; votre script l'écrit dans la bonne arborescence, puis déclenche l'événement attendu.

Pourquoi le CAPTCHA finit encapsulé

Le Shadow DOM isole l'arborescence interne d'un Web Component : dès qu'une équipe factorise ses formulaires, le widget CAPTCHA part avec eux.

Situation Ce qui encapsule le widget
Design system interne Formulaire de connexion devenu composant réutilisable
Architecture micro-frontends Chaque application isole son arborescence
Widget fourni par un prestataire Le prestataire encapsule tout le formulaire
Bibliothèque de composants métier Le CAPTCHA vit dans l'élément, pas dans la page

Cas typique : un éditeur SaaS lyonnais publie un composant <sso-login> embarquant Cloudflare Turnstile, partagé entre le portail client et le back-office. Les tests de bout en bout, verts depuis des mois, échouent le jour où le design system déplace le formulaire dans un shadow root : le sélecteur n'a pas bougé, mais il ne voit plus rien.

Repérer le shadow host avant d'écrire un sélecteur

Dans l'inspecteur Chrome ou Firefox, la mention #shadow-root (open) ou #shadow-root (closed) au-dessus du widget tranche immédiatement. Notez la balise hôte : c'est cet élément, et non le CAPTCHA, que votre test doit attendre. Le symptôme est sans ambiguïté : le widget s'affiche, mais document.querySelector renvoie null.

Localiser l'élément dans l'arborescence encapsulée

Utilisez host.shadowRoot.querySelector pour atteindre l'élément interne. Playwright traverse les frontières nativement (API locator, sélecteur de perçage >>) ; Selenium expose shadow_root sur l'élément hôte ; Puppeteer passe par page.evaluate et une fonction récursive.

Quand les composants sont imbriqués, parcourez récursivement chaque nœud possédant un shadowRoot, mais bornez la profondeur et ciblez les balises connues : une récursion sur tout le document ralentit la suite de tests.

Injecter le token et déclencher le callback

Une fois l'élément localisé, écrivez le token dans le champ de réponse du widget, puis déclenchez l'événement attendu. Poser la valeur sans déclencher l'événement est l'erreur la plus fréquente : le formulaire reste bloqué, sans message d'erreur.

Exemple JS d'injection :

const root = document.querySelector('my-form').shadowRoot;
root.getElementById('g-recaptcha-response').value = token;
root.querySelector('form').dispatchEvent(new Event('submit'));

Adaptez le sélecteur au type rendu par votre composant et lisez le pageurl au moment de l'extraction, jamais en dur : un token émis pour une URL et soumis sur une autre est rejeté.

Racine ouverte, racine fermée : ce qui change

Cas Ce que vous observez Approche
Un seul niveau <custom-form> puis le widget Requête directe sur shadowRoot
Deux niveaux ou plus Coquille applicative puis formulaire Traversée récursive bornée
Racine ouverte el.shadowRoot est exploitable Cas nominal
Racine fermée el.shadowRoot vaut null Forcer attachShadow({mode:'open'}) avant le chargement, sur votre application

Forcer l'ouverture d'une racine fermée n'a de sens que sur du code que vous maîtrisez ; si le composant vient d'un prestataire, demandez-lui un mode ouvert en préproduction.

Dépannage

Problème Cause Correctif
Sélecteur null, widget visible Requête lancée depuis document Passer par le shadowRoot de l'hôte
shadowRoot vaut null Racine fermée Forcer le mode ouvert au chargement, sur votre application
Token accepté par l'API, refusé par le formulaire pageurl figé dans le script Lire l'URL réelle à l'extraction
Champ rempli, formulaire non soumis Callback jamais déclenché Déclencher l'événement après l'écriture
Widget absent au chargement Composant hydraté en asynchrone Attendre la balise hôte, pas le widget
Suite de tests plus lente Récursion sur tout le document Borner la profondeur, cibler les balises connues

Observabilité et journalisation

Tracez la durée d'obtention du token, le code retour HTTP et l'identifiant de tâche, plus une métrique propre au Shadow DOM : le temps de localisation de l'élément, qui explose lors d'une refonte du design system.

Côté conformité, appliquez le principe de minimisation du RGPD : les captures de formulaires de connexion contiennent souvent des données personnelles. Tronquez les tokens dans les logs, purgez les artefacts de CI et gardez vos exécutions dans une région proche de vos équipes (eu-west-3 à Paris, OVHcloud ou Scaleway).

Cette instrumentation ne change rien à votre facture : CaptchaAI facture au thread simultané, avec des résolutions illimitées par thread. Une suite nocturne tient dans BASIC ($15/mois, 5 threads) ; des parcours rejoués à chaque merge request appellent plutôt STANDARD ($30/mois, 15 threads).

Liste de contrôle avant de fusionner

  • Périmètre limité à vos applications ou à des environnements autorisés par écrit.

  • Clé CaptchaAI dans un secret CI ou un coffre, jamais dans le code source.

  • Le test attend la balise hôte, puis le widget dans le shadow root.

  • Profondeur de récursion bornée, balises hôtes connues ciblées en priorité.

  • Durées d'appel et codes retour tracés à chaque exécution.

  • Retry idempotent avec backoff exponentiel borné sur les erreurs transitoires.

FAQ

Le Shadow DOM change-t-il quelque chose à l'appel API CaptchaAI ?

Non. L'API a besoin du sitekey et du pageurl ; l'endroit où le widget est rendu ne la concerne pas. L'encapsulation complique seulement la lecture du sitekey et l'écriture du token.

Le token est injecté mais le formulaire refuse de partir : que vérifier ?

Le callback du composant n'a pas été déclenché. Écrire la valeur ne suffit jamais : déclenchez l'événement attendu, ou appelez la fonction de callback exposée par le widget juste après.

Comment faire tourner ces tests dans une CI hébergée en Europe ?

Comme n'importe quel test de navigateur : un runner Linux, la clé API en secret et une région proche de vos équipes. Le Shadow DOM n'impose aucune configuration CI particulière.

CaptchaAI prend-il en charge hCaptcha dans ces composants ?

Non — hCaptcha n'est pas pris en charge, pas plus que FunCaptcha (Arkose Labs). Les types couverts sont reCAPTCHA v2 et v3, Cloudflare Turnstile et Challenge, GeeTest v3, les CAPTCHA image et en grille, plus CaptchaFox (bêta), Friendly Captcha (bêta) et Lemin (bêta). GeeTest v4 est annoncé comme à venir.

Puis-je appliquer la méthode à des sites que je ne contrôle pas ?

Non. Tous les exemples portent sur vos applications ou sur des environnements autorisés par écrit. Si une source externe entre dans le projet, vérifiez ses conditions d'utilisation et votre base juridique.

Guides connexes

Vos composants encapsulés méritent des tests aussi fiables que le reste de l'application. – Obtenez votre clé CaptchaAI.

Les commentaires sont désactivés pour cet article.