حالات الاستخدام

التعامل مع اختبار CAPTCHA لاختبار تسجيل المقررات الجامعية

تستخدم أنظمة تسجيل الدورات الجامعية reCAPTCHA v2 لحماية نماذج التسجيل وصفحات البحث في الفصل الدراسي ومخططي الجدول الزمني. تحتاج فرق ضمان الجودة التي تختبر هذه الأنظمة إلى التعامل مع اختبارات CAPTCHA عبر سيناريوهات اختبار متعددة - سير عمل التسجيل، ومنطق قائمة الانتظار، وتوافر المقاعد، وتنفيذ الموعد النهائي. وإليك كيفية دمج حل اختبار CAPTCHA في اختبار التسجيل.

أين تظهر اختبارات CAPTCHA في أنظمة التسجيل

خطوة التسجيل نوع التحقق الغرض
صفحة تسجيل دخول الطالب reCAPTCHA v2 منع حشو أوراق الاعتماد
البحث عن الدورة /browse reCAPTCHA v2 الحد من عمليات البحث الآلية
إضافة دورات /drop reCAPTCHA v2 منع التسجيل الآلي
تسجيل قائمة الانتظار reCAPTCHA v2 منع روبوتات التسجيل التلقائي
مخطط الجدول الزمني Cloudflare Turnstile حماية الأدوات التفاعلية
طلبات النسخ reCAPTCHA v2 التحقق من المستخدم البشري

اختبار التسجيل مع حل اختبار CAPTCHA

import requests
import time
import re

class RegistrationTester:
    def __init__(self, api_key, portal_url):
        self.api_key = api_key
        self.portal_url = portal_url
        self.session = requests.Session()
        self.session.headers.update({
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
        })

    def test_login(self, student_id, password):
        """Test login flow with CAPTCHA handling."""
        login_page = self.session.get(f"{self.portal_url}/login")

        site_key = self._extract_site_key(login_page.text)
        token = self._solve_recaptcha(site_key, f"{self.portal_url}/login")

        response = self.session.post(f"{self.portal_url}/login", data={
            "studentId": student_id,
            "password": password,
            "g-recaptcha-response": token
        })

        return {
            "status": response.status_code,
            "redirected": response.url != f"{self.portal_url}/login",
            "success": "dashboard" in response.url or response.status_code == 200
        }

    def test_course_search(self, term, department, level=None):
        """Test course search returns correct results."""
        params = {"term": term, "dept": department}
        if level:
            params["level"] = level

        response = self.session.get(
            f"{self.portal_url}/courses/search", params=params
        )

        if self._has_captcha(response.text):
            site_key = self._extract_site_key(response.text)
            token = self._solve_recaptcha(
                site_key, f"{self.portal_url}/courses/search"
            )
            response = self.session.post(
                f"{self.portal_url}/courses/search",
                data={**params, "g-recaptcha-response": token}
            )

        courses = self._parse_courses(response.text)
        return {
            "count": len(courses),
            "courses": courses,
            "all_match_dept": all(
                c["department"] == department for c in courses
            )
        }

    def test_enrollment(self, course_id, section):
        """Test enrolling in a course section."""
        enroll_page = self.session.get(
            f"{self.portal_url}/enroll/{course_id}/{section}"
        )

        if self._has_captcha(enroll_page.text):
            site_key = self._extract_site_key(enroll_page.text)
            token = self._solve_recaptcha(
                site_key,
                f"{self.portal_url}/enroll/{course_id}/{section}"
            )
        else:
            token = None

        form_data = {
            "courseId": course_id,
            "section": section,
            "confirm": "true"
        }
        if token:
            form_data["g-recaptcha-response"] = token

        response = self.session.post(
            f"{self.portal_url}/enroll/submit",
            data=form_data
        )

        return {
            "status": response.status_code,
            "enrolled": "success" in response.text.lower(),
            "waitlisted": "waitlist" in response.text.lower(),
            "error": self._extract_error(response.text)
        }

    def test_seat_availability(self, course_ids):
        """Test seat availability display for multiple courses."""
        results = []
        for course_id in course_ids:
            response = self.session.get(
                f"{self.portal_url}/courses/{course_id}"
            )

            if self._has_captcha(response.text):
                site_key = self._extract_site_key(response.text)
                token = self._solve_recaptcha(
                    site_key, f"{self.portal_url}/courses/{course_id}"
                )
                response = self.session.post(
                    f"{self.portal_url}/courses/{course_id}",
                    data={"g-recaptcha-response": token}
                )

            availability = self._parse_availability(response.text)
            results.append({"course_id": course_id, **availability})

        return results

    def _has_captcha(self, html):
        return "g-recaptcha" in html or "recaptcha" in html.lower()

    def _extract_site_key(self, html):
        match = re.search(r'data-sitekey="([^"]+)"', html)
        if match:
            return match.group(1)
        raise ValueError("reCAPTCHA site key not found")

    def _solve_recaptcha(self, site_key, page_url):
        resp = requests.post("https://ocr.captchaai.com/in.php", data={
            "key": self.api_key,
            "method": "userrecaptcha",
            "googlekey": site_key,
            "pageurl": page_url,
            "json": 1
        })
        task_id = resp.json()["request"]

        for _ in range(60):
            time.sleep(3)
            result = requests.get("https://ocr.captchaai.com/res.php", params={
                "key": self.api_key,
                "action": "get",
                "id": task_id,
                "json": 1
            })
            data = result.json()
            if data["status"] == 1:
                return data["request"]

        raise TimeoutError("reCAPTCHA solve timed out")

    def _parse_courses(self, html):
        from bs4 import BeautifulSoup
        soup = BeautifulSoup(html, "html.parser")

        def text_or_empty(node):
            return node.text.strip() if node and node.text else ""

        courses = []
        for row in soup.select(".course-row, tr.course"):
            courses.append({
                "id": row.get("data-course-id", ""),
                "department": text_or_empty(row.select_one(".dept")),
                "number": text_or_empty(row.select_one(".number")),
                "title": text_or_empty(row.select_one(".title")),
                "seats": text_or_empty(row.select_one(".seats"))
            })
        return courses

    def _parse_availability(self, html):
        from bs4 import BeautifulSoup
        soup = BeautifulSoup(html, "html.parser")

        def text_or_none(node):
            return node.text.strip() if node and node.text else None

        return {
            "total_seats": text_or_none(soup.select_one(".total-seats")),
            "enrolled": text_or_none(soup.select_one(".enrolled-count")),
            "available": text_or_none(soup.select_one(".available-seats")),
            "waitlist": text_or_none(soup.select_one(".waitlist-count"))
        }

    def _extract_error(self, html):
        from bs4 import BeautifulSoup
        soup = BeautifulSoup(html, "html.parser")
        error = soup.select_one(".error-message, .alert-danger")
        return error.text.strip() if error else None

مشغل مجموعة الاختبار (JavaScript)

class RegistrationTestSuite {
  constructor(apiKey, portalUrl) {
    this.apiKey = apiKey;
    this.portalUrl = portalUrl;
    this.results = [];
  }

  async runAll(testCredentials) {
    const tests = [
      () => this.testLogin(testCredentials),
      () => this.testCourseSearch('Fall2025', 'CS'),
      () => this.testEnrollment('CS101', '001'),
      () => this.testDropCourse('CS101', '001'),
      () => this.testWaitlistFlow('CS201', '001')
    ];

    for (const test of tests) {
      try {
        const result = await test();
        this.results.push({ ...result, passed: true });
      } catch (error) {
        this.results.push({ name: test.name, error: error.message, passed: false });
      }
    }

    return this.generateReport();
  }

  async solveRecaptcha(pageUrl, html) {
    const match = html.match(/data-sitekey="([^"]+)"/);
    if (!match) return null;

    const submitResp = await fetch('https://ocr.captchaai.com/in.php', {
      method: 'POST',
      body: new URLSearchParams({
        key: this.apiKey,
        method: 'userrecaptcha',
        googlekey: match[1],
        pageurl: pageUrl,
        json: '1'
      })
    });
    const { request: taskId } = await submitResp.json();

    for (let i = 0; i < 60; i++) {
      await new Promise(r => setTimeout(r, 3000));
      const result = await fetch(
        `https://ocr.captchaai.com/res.php?key=${this.apiKey}&action=get&id=${taskId}&json=1`
      );
      const data = await result.json();
      if (data.status === 1) return data.request;
    }
    throw new Error('reCAPTCHA solve timed out');
  }

  generateReport() {
    const passed = this.results.filter(r => r.passed).length;
    const failed = this.results.filter(r => !r.passed).length;

    return {
      total: this.results.length,
      passed,
      failed,
      passRate: `${((passed / this.results.length) * 100).toFixed(1)}%`,
      details: this.results
    };
  }
}

// Usage
const suite = new RegistrationTestSuite('YOUR_API_KEY', 'https://register.university.edu');
const report = await suite.runAll({ studentId: 'test123', password: 'testpass' });
console.log(`Tests: ${report.passed}/${report.total} passed (${report.passRate})`);

سيناريوهات الاختبار التي تقوم بتشغيل اختبار CAPTCHA

سيناريو الاختبار تردد اختبار CAPTCHA تأثير
تسجيل الدخول بحسابات متعددة عالية تؤدي كل محاولة تسجيل دخول إلى تشغيل اختبار CAPTCHA
اختبارات التسجيل المتزامنة عالية حماية ضد البوت
عمليات البحث السريعة للدورة معتدل استعلامات محدودة السعر
إنشاء الجدول الزمني منخفض التفاعل في جلسة واحدة
التحقق من صحة الشرط المسبق منخفض جزء من تدفق التسجيل

استكشاف الأخطاء وإصلاحها

المشكلة السبب الإجراء
تظهر اختبارات CAPTCHA كثيرة خلال فترة قصيرة إيقاع الجمع أو مستوى التوازي أعلى من اللازم بالنسبة للمصدر خفّض التكرار وحافظ على استقرار الجلسات وراجع جودة الوكيل
البيانات ناقصة حتى بعد حل CAPTCHA أداة الاستخراج تقرأ عرضًا قديمًا أو غير مكتمل استخرج البيانات بعد تطبيق الرمز المميز داخل الجلسة نفسها
ترتفع التكلفة أسرع من المتوقع تؤدي كثرة إعادة المحاولات أو الصفحات غير الضرورية إلى تحديات إضافية اقصر الحل على الخطوات الحرجة وتتبع إعادة المحاولات لكل مصدر

الخطوات التالية

أدلة ذات صلة

التعليقات غير مفعّلة لهذا المقال.