يفشل اختبار Puppeteer أو ينجح في أغلب الأحيان بسبب سياق المتصفح، لا بسبب المكتبة نفسها. حين تختلف دقة العرض أو اللغة أو ملفات تعريف الارتباط بين جهازك المحلي وخط أنابيب CI، قد يظهر تحدي CAPTCHA في بيئة ويغيب في أخرى، فتضيع ساعات في مطاردة عطل غير موجود. المسار العملي واضح: ثبّت إعداد Puppeteer، شغّل CaptchaAI داخل الصفحة التي تملكها، وسجّل كل إشارة قبل أن تحاول تفسير السبب. هذا الدليل يشرح ذلك المسار خطوة بخطوة لفرق QA التي تختبر تطبيقاتها الخاصة.
حدود الاستخدام: هذا الدليل مخصّص لتشغيل Puppeteer داخل بيئة تملكها ولديك تصريح باختبارها، بهدف إعادة إنتاج سلوك الصفحة وقراءة التحديات وتسجيل الأعطال بدقة. لا يتناول إخفاء الأتمتة ولا تغيير هوية المتصفح على مواقع لا تملكها.
متى يناسب فريقك دمج Puppeteer مع CaptchaAI؟
يصلح هذا النمط عندما:
- تملك الموقع أو بيئة staging التي تختبرها.
- تحتاج إلى تكرار المسار نفسه مراراً أثناء التطوير أو داخل CI.
- تريد تحديد ما إذا كان الخلل في استخراج
sitekey، أو في حقن الرمز، أو في منطق الإرسال ذاته. - تسعى إلى تقليص الفروق العرضية بين الأجهزة، لا إلى تغيير هوية المتصفح.
مثال واقعي: متجر إلكتروني في الرياض
خذ مثالاً واقعياً: فريق QA في متجر إلكتروني بالرياض يختبر صفحة إتمام الشراء في بيئة staging محمية بـ reCAPTCHA v2. تعمل الحزمة الاختبارية على حاسوب المطوّر دون مشكلات، ثم تفشل على خادم CI لأن دقة العرض واللغة الافتراضية مختلفتان، فيظهر التحدي في بيئة ويغيب في الأخرى. تثبيت السياق يزيل هذا التباين قبل أن يبدأ الفريق البحث عن سبب لا وجود له. إذا كان هدفك مختلفاً عن ذلك، فلا ينبغي اعتماد هذا الملف مرجعاً للتشغيل.
تثبيت إعداد Puppeteer المتسق
ابدأ بتوحيد نافذة المتصفح ومسار البيانات والسجلات بحيث يمكن إعادة تنفيذ كل تشغيل بالطريقة نفسها. المثال التالي يعتمد Puppeteer القياسي فقط، دون إضافات، ودون أعلام موجّهة للالتفاف على الضوابط:
const fs = require("fs");
const path = require("path");
const puppeteer = require("puppeteer");
const USER_DATA_DIR = path.resolve("./tmp/qa-profile");
async function launchQaBrowser() {
fs.mkdirSync(USER_DATA_DIR, { recursive: true });
const browser = await puppeteer.launch({
headless: false,
userDataDir: USER_DATA_DIR,
defaultViewport: {
width: 1440,
height: 900,
deviceScaleFactor: 1,
},
args: [
"--window-size=1440,900",
"--lang=en-US",
"--no-sandbox",
],
});
const page = await browser.newPage();
await page.setExtraHTTPHeaders({
"Accept-Language": "en-US,en;q=0.9",
});
return { browser, page };
}
الغاية هنا ليست جعل Puppeteer "خفياً"، بل ضمان مرور كل تشغيل بالإعداد ذاته حتى تصبح مقارنة النتائج بين يوم وآخر، أو بين بيئة محلية وأخرى على CI، عادلة وذات معنى. لاحظ أن مسار USER_DATA_DIR يحافظ على الجلسة بين التشغيلات، فلا تبدأ من ملف تعريف فارغ في كل مرة، وهو ما يقلّل بدوره الفروق التي قد تستدعي تحدياً إضافياً.
دمج CaptchaAI: قراءة التحدي وحقن الرمز
بعد فتح الصفحة المملوكة، افحص العناصر المعروفة ثم مرّر الرمز الناتج إلى النموذج ضمن الجلسة نفسها. يناسب هذا النمط اختبار تطبيقك أو نسخة staging تتبع لك:
async function detectCaptcha(page) {
return page.evaluate(() => {
const recaptcha = document.querySelector("[data-sitekey]");
if (recaptcha) {
return {
type: "recaptcha_v2",
sitekey: recaptcha.getAttribute("data-sitekey"),
pageUrl: window.location.href,
};
}
const turnstile = document.querySelector(".cf-turnstile[data-sitekey]");
if (turnstile) {
return {
type: "turnstile",
sitekey: turnstile.getAttribute("data-sitekey"),
pageUrl: window.location.href,
};
}
return null;
});
}
async function injectToken(page, token) {
await page.evaluate((value) => {
const recaptchaField = document.querySelector("#g-recaptcha-response");
if (recaptchaField) {
recaptchaField.value = value;
recaptchaField.dispatchEvent(new Event("change", { bubbles: true }));
}
const turnstileField = document.querySelector("[name='cf-turnstile-response']");
if (turnstileField) {
turnstileField.value = value;
turnstileField.dispatchEvent(new Event("change", { bubbles: true }));
}
}, token);
}
حدِّد نقطة العطل قبل تغيير المكتبة
إذا تعثّر هذا المسار، فالمشكلة غالباً في الصفحة نفسها أو في نقطة الدمج، لا في الحاجة إلى حيلة مراوغة جديدة. لذلك احتفظ بسجل واضح يوثّق ثلاث إشارات تكشف مصدر العطل بسرعة:
- نوع التحدي الذي اكتشفته الصفحة فعلاً
- وجود حقول الإدخال المتوقعة من عدمه
- نتيجة الإرسال النهائية كما يراها الخادم
ما الإشارات التي يجب تسجيلها أثناء الاختبار؟
السجل الجيد يمنح الفريق تفسيراً هندسياً قابلاً للتكرار، بدل الاكتفاء بانطباعات عامة عن سلوك المتصفح لا تساعد على تشخيص عطل بعينه. سجّل الحقول التالية في كل تشغيل:
| الإشارة | سبب تسجيلها |
|---|---|
| عنوان الصفحة ومسارها | لتحديد إن كانت المشكلة محصورة في مسار واحد |
| نوع CAPTCHA | لكشف أي تغيّر في طبقة الحماية نفسها |
زمن استخراج sitekey |
لقياس تأخّر تحميل عناصر الصفحة |
| زمن الحل | لمقارنة الأداء ضمن البيئة والحمل نفسيهما |
| نتيجة الإرسال | لتمييز الرفض القادم من الخادم عن رفض الصفحة |
| معرّف الجلسة أو ملف التعريف | لإعادة تشغيل السيناريو نفسه لاحقاً |
حدود هذا الدليل ومتى لا يصلح
ما لا يتناوله هذا الدليل
لا يتناول هذا المحتوى:
- مكوّنات إضافية للمراوغة أو الإخفاء
- تعديل خصائص المتصفح أو النظام لمحاكاة هوية مختلفة
- تدوير البصمات أو الوكلاء أو السياقات للعمل على موقع لا تملكه
- أحكاماً تسويقية غير قابلة للتحقق حول سلوك المتصفح
ابدأ بتثبيت ما تملكه أولاً
إذا احتاج فريقك إلى تشغيل بلا واجهة مستخدم، فابدأ أولاً بتثبيت المدخلات التي تملكها: دقة العرض، اللغة، زمن التحميل، ملفات تعريف الارتباط الصادرة من تطبيقك، وترتيب الخطوات. هذه العوامل تفسّر معظم الفروق التي تظهر في بيئات QA، وتغنيك عن البحث في اتجاهات لا علاقة لها بالعطل الفعلي.
الأسئلة الشائعة
هل أحتاج إلى إضافات مثل puppeteer-extra لهذا المسار؟
لا. طالما أنك تختبر بيئة تملكها، ابدأ بـ Puppeteer القياسي بإعدادات ثابتة وقابلة للتكرار؛ الإضافات تزيد التعقيد دون أن ترفع دقة التشخيص في هذه الحالة.
كيف أميّز أن سبب الفشل هو الصفحة لا خدمة الحل؟
راجع السجل: إذا نجح استخراج sitekey ووصل الرمز لكن رُفض الإرسال، فالمشكلة في منطق الصفحة أو الخادم. أما غياب الحقول أو فشل الاستخراج فيشير إلى الصفحة نفسها.
هل تختلف طريقة حقن الرمز بين reCAPTCHA وTurnstile؟
نعم. يُكتب رمز reCAPTCHA في الحقل #g-recaptcha-response، بينما يُكتب رمز Turnstile في الحقل المسمّى cf-turnstile-response. المثال أعلاه يعالج الحالتين ضمن الدالة نفسها.
ماذا أفعل إذا لم يظهر التحدي على CI أصلاً؟
ثبّت دقة العرض واللغة وملفات تعريف الارتباط لتطابق بيئتك الفعلية، ثم أعد التشغيل. اختلاف السياق وحده كفيل بإظهار التحدي في بيئة وإخفائه في أخرى.
أدلة ذات صلة
- أنماط متقدمة لدمج Puppeteer مع CaptchaAI في Node.js
- الوضع الرأسي مقابل عديم الواجهة في حل اختبار CAPTCHA
عند اختبار تطبيق تملكه بواسطة Puppeteer، ابدأ بتثبيت سياق الجلسة وتسجيل الإشارات التي تراها الصفحة، ثم أدخِل CaptchaAI في المسار نفسه وداخل البيئة ذاتها.