Reference

Chrome DevTools Protocol + CaptchaAI : diagnostic CAPTCHA dans vos environnements de test

Périmètre : ce guide s'applique uniquement à vos propres environnements QA et staging, ou à des environnements explicitement autorisés. Il décrit des étapes de diagnostic et de vérification pour votre propre intégration CAPTCHA, pas pour des sites tiers ni pour des workflows non autorisés.

Quand un CAPTCHA fait échouer un test de staging, la cause est rarement lisible depuis l'interface : le widget s'est-il chargé, avec quel sitekey, et la réponse est-elle remontée jusqu'au backend ? Le Chrome DevTools Protocol (CDP) répond à ces questions en exposant l'activité réseau et l'état du DOM du navigateur. Associé à CaptchaAI, il ne sert pas à automatiser : c'est une surface de diagnostic qui relie chaque maillon de votre intégration, du widget jusqu'à la validation côté serveur.


À quoi sert CDP dans un diagnostic CAPTCHA

CDP tranche des questions que l'interface seule laisse ouvertes : le widget se charge-t-il au bon moment, la page utilise-t-elle le bon sitekey, la vérification atteint-elle le backend avec les bons paramètres, et les codes d'erreur suivent-ils le contrat de test ? Aucune logique de dissimulation n'entre en jeu : tout reste traçable chez vous. Sous RGPD, vérifiez aussi que vos journaux de test ne capturent aucune donnée personnelle superflue.


Lire le trafic réseau du widget avec CDP

Une session CDP expose plusieurs signaux à la fois :

Signal observé Intérêt en QA À contrôler
Requête réseau du widget Confirme que le script CAPTCHA se charge Hôte, paramètres, code de statut
État DOM du widget Vérifie sitekey et callback data-sitekey, conteneur, état d'erreur
Requête de vérification Trace le chemin backend Endpoint, identifiant de requête, latence
Réponse serveur Explique la réussite ou l'échec du test success, code d'erreur, métadonnées staging

Une configuration minimale suffit pour ouvrir cette session sur un Chrome lancé en mode debug, par exemple sur un agent de CI ou une instance de staging chez OVHcloud :

import http from "node:http";
import WebSocket from "ws";

async function connectToCdp(port = 9222) {
  const targets = await new Promise((resolve, reject) => {
    http.get(`http://127.0.0.1:${port}/json/list`, (res) => {
      let body = "";
      res.on("data", (chunk) => (body += chunk));
      res.on("end", () => resolve(JSON.parse(body)));
    }).on("error", reject);
  });

  const pageTarget = targets.find((target) => target.type === "page");
  const ws = new WebSocket(pageTarget.webSocketDebuggerUrl);

  await new Promise((resolve, reject) => {
    ws.once("open", resolve);
    ws.once("error", reject);
  });

  let id = 0;
  const send = (method, params = {}) => {
    const requestId = ++id;
    ws.send(JSON.stringify({ id: requestId, method, params }));
    return requestId;
  };

  send("Page.enable");
  send("Runtime.enable");
  send("Network.enable");

  return { ws, send };
}

Extraire le sitekey depuis la page de staging

Plutôt que de le supposer, lisez le sitekey directement depuis la page de staging : vous repérez les dérives de configuration au plus tôt.

async function readSitekey(pageUrl) {
  const targetResponse = await fetch(
    "http://127.0.0.1:9222/json/new?" + encodeURIComponent(pageUrl)
  );
  const target = await targetResponse.json();
  const ws = new WebSocket(target.webSocketDebuggerUrl);

  await new Promise((resolve, reject) => {
    ws.once("open", resolve);
    ws.once("error", reject);
  });

  ws.send(JSON.stringify({ id: 1, method: "Runtime.evaluate", params: {
    expression: `(() => {
      const widget = document.querySelector('[data-sitekey]');
      if (!widget) return null;
      return {
        sitekey: widget.getAttribute('data-sitekey'),
        widgetType: widget.className,
        pageUrl: location.href,
      };
    })()`,
    returnByValue: true,
  }}));

  return await new Promise((resolve) => {
    ws.on("message", (payload) => {
      const message = JSON.parse(payload);
      if (message.id === 1) {
        resolve(message.result.result.value);
        ws.close();
      }
    });
  });
}

Au-delà de la commande, c'est l'alignement avec votre configuration de staging qui compte :

Contrôle sur le sitekey Attendu
Environnement Le sitekey correspond à l'environnement ciblé
Portée Le widget n'apparaît que sur les formulaires prévus
Callback / action Le nom figure dans vos données de test

Déclencher une résolution CaptchaAI dans le test

Une fois la page et le sitekey confirmés, déclenchez une tâche CaptchaAI dans le même test et intégrez son résultat à la chaîne de diagnostic.

async function submitCaptchaTask({ apiKey, pageUrl, sitekey }) {
  const body = new URLSearchParams({
    key: apiKey,
    method: "userrecaptcha",
    googlekey: sitekey,
    pageurl: pageUrl,
    json: "1",
  });

  const submit = await fetch("https://ocr.captchaai.com/in.php", {
    method: "POST",
    body,
  });
  const submitJson = await submit.json();

  if (submitJson.status !== 1) {
    throw new Error(`CaptchaAI submit failed: ${submitJson.request}`);
  }

  for (let attempt = 0; attempt < 30; attempt += 1) {
    await new Promise((resolve) => setTimeout(resolve, 5000));
    const poll = await fetch(
      `https://ocr.captchaai.com/res.php?key=${apiKey}&action=get&id=${submitJson.request}&json=1`
    );
    const pollJson = await poll.json();
    if (pollJson.status === 1) {
      return pollJson.request;
    }
  }

  throw new Error("CaptchaAI result timeout in QA run");
}

L'objectif reste de retracer toute la chaîne : widget détecté, tâche envoyée, token reçu. CaptchaAI couvre ici reCAPTCHA v2 et v3, Cloudflare Turnstile et Cloudflare Challenge ; la validation finale se fait contre votre endpoint de test.


Contrôler la validation côté backend

L'étape décisive n'est souvent pas dans le navigateur, mais dans votre chemin de vérification. Faites en sorte que la suite QA enregistre la réponse backend avec un identifiant de requête.

async function verifyInQaBackend({ token, testRunId }) {
  const response = await fetch("https://staging.example-app.test/qa/captcha/verify", {
    method: "POST",
    headers: { "content-type": "application/json" },
    body: JSON.stringify({
      token,
      testRunId,
      expectedAction: "signup",
      environment: "staging",
    }),
  });

  if (!response.ok) {
    throw new Error(`QA verify endpoint returned ${response.status}`);
  }

  return response.json();
}

Une bonne réponse QA contient plus qu'un success: true ; elle vous laisse rejouer chaque cas sans toucher à la production :

Champ de la réponse QA Ce qu'il permet de vérifier
Identifiant de requête ou de trace Rejouer un cas de test précis
Action ou identifiant de widget Confirmer que le bon widget a été validé
Temps de réponse du backend Suivre la latence du chemin de vérification
Code d'erreur Couvrir les cas de test négatifs
Environnement (staging, qa, preprod) Éviter toute confusion avec la production

Dépannage

Problème Cause probable Correctif
Le widget ne se charge pas Mauvais script ou feature flag Inspecter les requêtes réseau de staging
Le sitekey ne correspond pas Dérive d'environnement Comparer la valeur déployée et celle lue dans le DOM
L'endpoint QA renvoie 400 Champs attendus manquants Aligner le journal backend sur votre schéma
Timeout pendant le polling File saturée en test Augmenter le délai et mesurer la charge à part
Réponse backend incohérente Données de test variables Fixer des comptes et des fixtures reproductibles

Questions fréquentes

CDP remplace-t-il un outil de test end-to-end comme Playwright ?

Non. CDP est une couche de diagnostic bas niveau, pas un framework de test. Vous l'utilisez en complément : Playwright ou Puppeteer pilotent le scénario, CDP observe le réseau et le DOM sous-jacents.

Peut-on intégrer ce diagnostic dans un pipeline CI ?

Oui. Lancez Chrome avec le port de debug distant sur l'agent CI, connectez la session CDP depuis le test, puis archivez les journaux réseau comme artefacts. Une instance de staging sur Scaleway ou en région AWS eu-west-3 (Paris) sert d'environnement de référence.

CaptchaAI ne prend-il en charge que reCAPTCHA v2 ?

Non. Il couvre reCAPTCHA v2 et v3, Cloudflare Turnstile et Cloudflare Challenge, GeeTest v3 et les CAPTCHA image/OCR. hCaptcha et FunCaptcha ne sont pas pris en charge.

Guides connexes

Reliez chaque maillon de votre intégration CAPTCHA à des données traçables — CaptchaAI facilite des exécutions reproductibles dans vos environnements.

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