CAPTCHA-виджет на staging-форме — частая причина, из-за которой end-to-end тест зависает и роняет прогон CI: браузер долистывает страницу до виджета и застревает, потому что решить его некому. Связка Puppeteer и CaptchaAI закрывает этот разрыв: Puppeteer управляет собственной staging-страницей, а CaptchaAI отдаёт токен для reCAPTCHA или Cloudflare Turnstile, который тест подставляет в форму и продолжает сценарий дальше. Ниже — рабочая схема для QA-инженера, который проверяет собственную интеграцию CAPTCHA, а не автоматизирует чужой сайт.
Область применения: это руководство — только для собственных или явно авторизованных QA-, staging- и production-сред. Ниже описаны диагностика и тестирование вашей собственной CAPTCHA-интеграции, а не сценарий для чужих сайтов и не для несанкционированных workflow.
Зачем CAPTCHA-тест нужен в QA-пайплайне
Ручная проверка формы регистрации или чекаута перед каждым релизом не масштабируется: релизы выходят чаще, чем тестировщик успевает вручную пройти все ветки сценария. Puppeteer запускает headless-браузер с изолированным профилем, повторяет действия пользователя один в один и пишет результат в CI-лог. Единственное место, где такой прогон обычно ломается, — интерактивный CAPTCHA-виджет: без валидного токена браузер не может отправить форму дальше. CaptchaAI закрывает именно этот шаг — тест получает токен по API и продолжает сценарий так, будто его прошёл живой пользователь.
Что можно автоматизировать, а что нет
- собственные или явно авторизованные staging-домены — не чужие production-сайты;
- фиктивные тестовые учётные записи, формы и платежи, помеченные как тестовые;
- внутренние QA endpoints, которые принимают токен и возвращают pass/fail;
- запись результата каждого прогона в собственный CI/CD pipeline.
Как настроить Puppeteer для QA-прогона
Первый шаг — поднять браузер с отдельным профилем под конкретную форму, чтобы куки и localStorage не пересекались между параллельными прогонами:
import puppeteer from 'puppeteer';
const PAGE = 'https://staging.example.com/qa-form';
export async function runQa() {
const browser = await puppeteer.launch({
headless: 'new',
userDataDir: './qa-profiles/qa-form',
});
const page = await browser.newPage();
await page.goto(PAGE, { waitUntil: 'networkidle0' });
return { browser, page };
}
userDataDir создаёт изолированный профиль на каждый сценарий: параллельные джобы CI не путают куки друг друга, а сам профиль можно удалить перед следующим прогоном, если тест завис на середине сценария.
Как найти CAPTCHA-виджет на своей staging-странице
Дальше тест ищет сам виджет и достаёт из него sitekey — без него отправлять запрос в CaptchaAI бессмысленно:
const sitekey = await page.$eval(
'.g-recaptcha, .cf-turnstile',
el => el.getAttribute('data-sitekey'),
);
if (!sitekey) throw new Error('sitekey не найден на staging-странице');
Если селектор ничего не находит, вероятная причина — виджет рендерится асинхронно уже после отрисовки формы; тогда перед проверкой нужно дождаться его появления через page.waitForSelector, а не опрашивать DOM сразу после page.goto.
Как отправить sitekey в CaptchaAI и получить токен
Когда sitekey найден, тест отправляет задачу на in.php и опрашивает res.php, пока CaptchaAI не вернёт готовый токен:
import fetch from 'node-fetch';
const KEY = process.env.CAPTCHAAI_KEY;
export async function solveRecaptcha(sitekey, pageUrl) {
const submit = await fetch('https://ocr.captchaai.com/in.php', {
method: 'POST',
body: new URLSearchParams({
key: KEY, method: 'userrecaptcha',
googlekey: sitekey, pageurl: pageUrl, json: '1',
}),
}).then(r => r.json());
const id = submit.request;
for (let i = 0; i < 40; i++) {
await new Promise(r => setTimeout(r, 3000));
const res = await fetch(`https://ocr.captchaai.com/res.php?key=${KEY}&action=get&id=${id}&json=1`)
.then(r => r.json());
if (res.status === 1) return res.request;
}
throw new Error(`timeout for task ${id}`);
}
Цикл опроса ограничен 40 попытками по 3 секунды — это около двух минут на задачу, с запасом даже для более тяжёлых reCAPTCHA-сценариев. Если лимит регулярно исчерпывается, дело обычно не в CaptchaAI, а в неверном pageurl или устаревшем sitekey на странице.
Проверка токена на собственном backend
Клиентский код не должен сам решать, прошёл тест или нет: после получения токена он отправляется на собственный QA endpoint, который выполняет серверную верификацию у провайдера CAPTCHA и уже на её основе пишет pass/fail в журнал прогона. Такой порядок закрывает очевидную дыру — доверять решению на стороне браузера рискованно: токен в DOM легко подставить вручную, минуя реальную проверку, и тест будет зелёным там, где формы на самом деле не работают.
Логи и метрики для сравнения релизов
Структурированные логи — единственный способ быстро понять, стал ли релиз медленнее решать CAPTCHA или чаще ловить ошибку:
import json, time, logging
log = logging.getLogger('captcha-qa')
def record(event: str, **fields) -> None:
payload = {'ts': time.time(), 'event': event, **fields}
log.info(json.dumps(payload, ensure_ascii=False))
Минимальный набор полей для каждой попытки: slug, captcha_type, task_id, wait_seconds, verify_status, env. Этого достаточно, чтобы построить дашборд с медианой, P90 и P99 по типу CAPTCHA и по среде — и сразу увидеть, если новый релиз внёс регрессию именно на Turnstile или именно на staging, а не на production-подобном стенде.
Типичные проблемы и на что смотреть
| Симптом | Что сделать |
|---|---|
| sitekey не найден | Проверьте, рендерится ли виджет в staging, и дождитесь его через waitForSelector |
| Токен невалиден | Сверьте pageurl и sitekey — токен привязан к конкретному URL |
| Бесконечный pending | Проверьте баланс потоков CaptchaAI в личном кабинете |
| Браузер не запускается | Удалите старый userDataDir — профиль мог остаться заблокированным |
| Тест падает только в CI | Сравните версию Chromium локально и в CI-образе |
Чек-лист перед merge
- Запрос отправляется только на собственные или явно авторизованные endpoints — не на чужой домен.
- Тестовые учётные записи, события и платежи помечены как фиктивные и не попадают в боевую аналитику.
- CAPTCHA-токен проверяется на собственном backend, а не принимается на веру клиентским кодом.
- Логи содержат
task_id, тип CAPTCHA, время ожидания и итоговый pass/fail. - Скрипт возвращает корректный exit code, чтобы CI мог принять решение автоматически, без ручного просмотра лога.
Пример: QA формы бронирования в команде из СНГ
Небольшая команда, которая тестирует форму бронирования перед каждым релизом в GitLab CI, обычно запускает 3–5 параллельных браузеров с разными тестовыми аккаунтами. Для такой нагрузки хватает плана BASIC ($15/мес, 5 потоков) — CaptchaAI тарифицирует по количеству одновременных задач, а не по числу решённых CAPTCHA, поэтому нужное число потоков можно посчитать заранее по количеству параллельных job'ов в пайплайне. Фиксированная цена в долларах удобна и фрилансеру, и агентству, которое выставляет счёт клиенту в другой валюте: стоимость QA-инфраструктуры не плавает вместе с курсом.
FAQ
Можно ли использовать этот подход на сторонних сайтах?
Нет. Схема рассчитана только на собственные или явно авторизованные среды. Для стороннего ресурса нужно письменное разрешение владельца — без него автоматизация чужой формы находится вне зоны, которую описывает это руководство.
Сколько потоков CaptchaAI нужно для параллельных прогонов?
Ориентируйтесь на число одновременных браузеров в CI: 5 параллельных job'ов — 5 потоков, то есть план BASIC. Если тестов больше или прогоны идут сразу по нескольким формам, посчитайте пиковую параллельность и берите план с запасом — свободные потоки просто не расходуются.
Что делать, если CaptchaAI вернул ошибку?
Залогируйте task_id, тип CAPTCHA и текст ошибки, повторите запрос с экспоненциальной задержкой и фиксируйте долю ошибок на дашборде. Стабильный рост доли ошибок — повод проверить sitekey и саму страницу, а не сразу списывать всё на провайдера.
Как сравнивать результаты между релизами?
Храните логи в одном формате и стройте отчёт по медиане, P90 и P99 на одинаковом наборе сценариев. Сравнивать имеет смысл только сопоставимые выборки в своей среде — разные staging-конфигурации дадут разные цифры даже при одинаковом коде приложения.
Нужен ли в CI отдельный тестовый API-ключ?
Используйте обычный рабочий API-ключ CaptchaAI, но храните его как секрет CI (GitHub Actions secrets, GitLab CI/CD variables), а не в коде репозитория. Риск снижает не тип ключа, а дисциплина: ключ не попадает в git, ротируется при подозрении на утечку, а тест обращается только к вашим собственным staging-формам.
Связанные материалы
- Быстрый старт CaptchaAI
- QA-тестирование CAPTCHA в авторизованных средах
- Проверка CAPTCHA API на собственных формах
- Почему браузерный тест падает, а API проходит
- reCAPTCHA v2 через API
- Cloudflare Turnstile через API
- GeeTest v3 через API
Готовы убрать CAPTCHA-виджет из списка причин красного билда? Получите ключ CaptchaAI и подключите его к QA-пайплайну.