التكاملات

السرو + CaptchaAI: اختبار E2E مع معالجة اختبار CAPTCHA

تحظر اختبارات CAPTCHA اختبارات E2E الآلية. يؤدي تعطيلها في التدريج إلى حدوث انجراف في البيئة - أخطاء تظهر فقط في الإنتاج حيث تكون اختبارات CAPTCHA نشطة. يتيح CaptchaAI لاختبارات Cypress التفاعل مع اختبارات CAPTCHA الحقيقية، مما يحافظ على بيئات الاختبار مماثلة للإنتاج.


لماذا لا نقوم فقط بتعطيل اختبارات CAPTCHA في الاختبارات؟

النهج خطر
تعطيل اختبار CAPTCHA في التدريج يفتقد أخطاء التكامل، ويشكل اختلافات في التدفق
استخدم مفاتيح الاختبار (التمرير دائمًا) لا يختبر حقن الرمز المميز ومعالجة رد الاتصال
حل باستخدام CaptchaAI اختبار تكافؤ الإنتاج الكامل

الإعداد

npm install cypress --save-dev

تكوين السرو

// cypress.config.js
const { defineConfig } = require("cypress");

module.exports = defineConfig({
  e2e: {
    baseUrl: "https://your-app.com",
    defaultCommandTimeout: 120000,
    responseTimeout: 120000,
    setupNodeEvents(on, config) {
      on("task", {
        solveCaptcha({ siteUrl, sitekey, type }) {
          return solveCaptchaTask(siteUrl, sitekey, type);
        },
      });
      return config;
    },
  },
  env: {
    CAPTCHAAI_KEY: "YOUR_API_KEY",
  },
});

معالج المهام CaptchaAI

// cypress/plugins/captcha-solver.js
const https = require("https");

function httpPost(url, data) {
  return new Promise((resolve, reject) => {
    const params = new URLSearchParams(data).toString();
    const options = {
      method: "POST",
      headers: { "Content-Type": "application/x-www-form-urlencoded" },
    };
    const req = https.request(url, options, (res) => {
      let body = "";
      res.on("data", (c) => (body += c));
      res.on("end", () => resolve(JSON.parse(body)));
    });
    req.on("error", reject);
    req.write(params);
    req.end();
  });
}

function httpGet(url) {
  return new Promise((resolve, reject) => {
    https.get(url, (res) => {
      let body = "";
      res.on("data", (c) => (body += c));
      res.on("end", () => resolve(JSON.parse(body)));
    }).on("error", reject);
  });
}

async function solveCaptchaTask(siteUrl, sitekey, type = "recaptcha_v2") {
  const API = "https://ocr.captchaai.com";
  const key = process.env.CAPTCHAAI_KEY || "YOUR_API_KEY";

  const submitData = {
    key,
    pageurl: siteUrl,
    json: "1",
  };

  if (type === "turnstile") {
    submitData.method = "turnstile";
    submitData.sitekey = sitekey;
  } else {
    submitData.method = "userrecaptcha";
    submitData.googlekey = sitekey;
  }

  const submitResp = await httpPost(`${API}/in.php`, submitData);

  if (submitResp.status !== 1) {
    throw new Error(`Submit failed: ${submitResp.request}`);
  }

  const taskId = submitResp.request;

  // Poll for result
  for (let i = 0; i < 60; i++) {
    await new Promise((r) => setTimeout(r, 5000));

    const params = new URLSearchParams({
      key,
      action: "get",
      id: taskId,
      json: "1",
    });

    const result = await httpGet(`${API}/res.php?${params}`);

    if (result.request === "CAPCHA_NOT_READY") continue;
    if (result.status !== 1) throw new Error(`Solve failed: ${result.request}`);

    return result.request; // The CAPTCHA token
  }

  throw new Error("CAPTCHA solve timeout");
}

module.exports = { solveCaptchaTask };

قم بالتوصيل إلى cypress.config.js

// cypress.config.js
const { solveCaptchaTask } = require("./cypress/plugins/captcha-solver");

module.exports = defineConfig({
  e2e: {
    setupNodeEvents(on, config) {
      on("task", {
        solveCaptcha({ siteUrl, sitekey, type }) {
          return solveCaptchaTask(siteUrl, sitekey, type);
        },
      });
    },
  },
});

أوامر مخصصة

// cypress/support/commands.js

Cypress.Commands.add("solveCaptcha", (options = {}) => {
  cy.get("[data-sitekey]", { timeout: 10000 }).then(($el) => {
    const sitekey = options.sitekey || $el.attr("data-sitekey");
    const siteUrl = options.siteUrl || cy.url();

    cy.url().then((url) => {
      cy.task("solveCaptcha", {
        siteUrl: url,
        sitekey,
        type: options.type || "recaptcha_v2",
      }).then((token) => {
        // Inject token
        cy.window().then((win) => {
          const responseEl = win.document.querySelector(
            "#g-recaptcha-response"
          );
          if (responseEl) {
            responseEl.value = token;
          }

          // Set all hidden response fields
          win.document
            .querySelectorAll('[name="g-recaptcha-response"]')
            .forEach((el) => {
              el.value = token;
            });

          // Trigger callback if exists
          if (win.___grecaptcha_cfg) {
            const clients = win.___grecaptcha_cfg.clients;
            for (const key in clients) {
              const client = clients[key];
              if (client && typeof client.callback === "function") {
                client.callback(token);
              }
            }
          }
        });
      });
    });
  });
});

Cypress.Commands.add("solveTurnstile", (options = {}) => {
  cy.get("[data-sitekey]", { timeout: 10000 }).then(($el) => {
    const sitekey = options.sitekey || $el.attr("data-sitekey");

    cy.url().then((url) => {
      cy.task("solveCaptcha", {
        siteUrl: url,
        sitekey,
        type: "turnstile",
      }).then((token) => {
        cy.window().then((win) => {
          const input = win.document.querySelector(
            'input[name="cf-turnstile-response"]'
          );
          if (input) input.value = token;
        });
      });
    });
  });
});

أمثلة على اختبار E2E

تدفق تسجيل الدخول باستخدام reCAPTCHA

// cypress/e2e/login.cy.js
describe("Login with reCAPTCHA", () => {
  it("should log in through a CAPTCHA-protected form", () => {
    cy.visit("/login");

    cy.get("#username").type("testuser");
    cy.get("#password").type("securepassword123");

    // Solve the CAPTCHA
    cy.solveCaptcha();

    // Submit
    cy.get('button[type="submit"]').click();

    // Verify login success
    cy.url().should("include", "/dashboard");
    cy.get(".welcome-message").should("contain", "Welcome, testuser");
  });
});

تدفق التسجيل

// cypress/e2e/register.cy.js
describe("Registration with CAPTCHA", () => {
  it("completes registration with all fields + CAPTCHA", () => {
    cy.visit("/register");

    cy.get("#first-name").type("Test");
    cy.get("#last-name").type("User");
    cy.get("#email").type("test@example.com");
    cy.get("#password").type("StrongPass!123");
    cy.get("#confirm-password").type("StrongPass!123");

    cy.solveCaptcha();

    cy.get("#register-btn").click();
    cy.url().should("include", "/verify-email");
  });
});

الخروج المحمي بTurnstile

describe("Checkout with Turnstile", () => {
  it("processes payment through Turnstile-protected checkout", () => {
    cy.visit("/cart");

    cy.get(".checkout-btn").click();
    cy.get("#card-number").type("4242424242424242");
    cy.get("#expiry").type("12/26");
    cy.get("#cvc").type("123");

    cy.solveTurnstile();

    cy.get("#pay-now").click();
    cy.get(".confirmation").should("contain", "Order confirmed");
  });
});

إعادة المحاولة ومعالجة الأخطاء

// cypress/support/commands.js

Cypress.Commands.add("solveCaptchaWithRetry", (options = {}) => {
  const maxRetries = options.retries || 3;

  function attempt(retryCount) {
    return cy.task("solveCaptcha", {
      siteUrl: options.siteUrl,
      sitekey: options.sitekey,
      type: options.type || "recaptcha_v2",
    }).then((token) => {
      if (!token && retryCount < maxRetries) {
        cy.log(`CAPTCHA retry ${retryCount + 1}/${maxRetries}`);
        cy.wait(2000);
        return attempt(retryCount + 1);
      }
      return token;
    });
  }

  return attempt(0);
});

التكامل CI/CD

GitHub Actions

name: E2E Tests
on: [push, pull_request]

jobs:
  cypress:
    runs-on: ubuntu-latest
    steps:

      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20

      - run: npm ci

      - name: Run Cypress tests
        uses: cypress-io/github-action@v6
        env:
          CAPTCHAAI_KEY: ${{ secrets.CAPTCHAAI_KEY }}
        with:
          wait-on: "http://localhost:3000"
          start: npm start

اختبار التكامل الهزلي

// For teams that also use Jest for API-level CAPTCHA tests
const { solveCaptchaTask } = require("../cypress/plugins/captcha-solver");

test("CaptchaAI solves reCAPTCHA v2", async () => {
  const token = await solveCaptchaTask(
    "https://www.google.com/recaptcha/api2/demo",
    "6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ-",
    "recaptcha_v2"
  );

  expect(token).toBeDefined();
  expect(token.length).toBeGreaterThan(50);
}, 120000);

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

المشكلة السبب الإجراء
cy.task timed out استغرق حل اختبار CAPTCHA وقتًا طويلاً زيادة taskTimeout في التكوين
تم رفض الرمز المميز انتهت صلاحيته قبل الحقن تقليل التأخير بين الحل والإرسال
لم يتم العثور على data-sitekey يتم تحميل اختبار CAPTCHA ديناميكيًا أضف cy.wait() صريحًا أو اعتراضًا
لم يتم تشغيل رد الاتصال اسم رد الاتصال المخصص افحص ___grecaptcha_cfg في DevTools
فشل CI، ويمر المحلية متغير env مفقود أضف CAPTCHAAI_KEY إلى أسرار CI

الأسئلة الشائعة

هل سيؤدي هذا إلى إبطاء مجموعة الاختبار الخاصة بي؟

يضيف كل حل اختبار CAPTCHA ما بين 15 إلى 30 ثانية. قم بإجراء اختبارات CAPTCHA في مجموعة منفصلة أو بالتوازي مع Cypress Cloud.

هل يمكنني استخدام هذا مع اختبار مكونات السرو؟

لا - لا تقوم اختبارات المكونات بتحميل الصفحات الحقيقية. استخدم هذا فقط لاختبارات E2E التي تصل إلى عناوين URL للصفحة الكاملة باستخدام اختبارات CAPTCHA الحقيقية.

هل يجب أن أختبر اختبارات CAPTCHA الحقيقية أم أسخر منها؟

اختبر باستخدام اختبارات CAPTCHA الحقيقية في مرحلة E2E. استخدم المحاكاة في اختبارات الوحدة. وهذا يضمن التكافؤ الكامل في الإنتاج.

هل يعمل CaptchaAI مع موازاة Cypress Cloud؟

نعم. يستدعي كل جهاز متوازي نفس مفتاح API. يعالج CaptchaAI الطلبات المتزامنة.


أدلة ذات صلة



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

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