TestCafe Tests में कैप्चा कैसे हल करें (Node.js SDK)

TestCafe में कैप्चा step ज़्यादातर frameworks की तुलना में छोटा है, क्योंकि TestCafe का test code पहले से ही Node में चलता है। आप solver को सीधे test file से कॉल करते हैं, फिर एक ClientFunction से token को page में लिख देते हैं। हालाँकि इन सबसे पहले एक चीज़ जाँचनी होती है, और इसमें ग़लती करना पूरी दोपहर बर्बाद कर देता है: आपका run native automation इस्तेमाल कर रहा है या पुराना URL rewriting proxy। proxy पर, solver के आसपास पहुँचने से बहुत पहले ही reCAPTCHA टूटा हुआ होता है।
आपको क्या चाहिए
- TestCafe 3.0 या नया और Node.js 18 या नया, साथ में CapSkip Node.js SDK।
- एक Chromium browser, यानी Chrome या Edge। Native automation में Firefox या Safari शामिल नहीं हैं।
- जिस form का test हो रहा है उसका page URL, और उसकी sitekey।
- Local mode में चलता CapSkip, जब test runner और solver एक ही machine साझा करते हैं, या Server mode में जब वे ऐसा नहीं करते। दोनों modes का वर्णन यहाँ है: कनेक्शन सेटिंग्स.
# npm install capskip npm install --save-dev testcafe npm install capskip
पहले यह जाँचें: native automation या proxy?
TestCafe के पास browser चलाने के दो तरीक़े हैं और कैप्चा के मामले में वे बिल्कुल अलग बर्ताव करते हैं। मूल तरीक़ा hammerhead नाम का एक web proxy है। यह browser और site के बीच बैठता है, हर page में अपने automation scripts inject करता है, और resource के हर URL को दोबारा लिखता है ताकि वह वापस proxy की ओर इशारा करे। इसी ने TestCafe को बिना किसी driver के किसी भी browser का समर्थन करने दिया, और यही reCAPTCHA को तोड़ता भी है।
proxy पर दो विफलताएँ सामने आती हैं, दोनों hammerhead के विरुद्ध रिपोर्ट की गई हैं और दोनों में से कोई भी आपके test से ठीक नहीं होती। reCAPTCHA, Google के origin से एक web worker शुरू करने की कोशिश करता है और browser मना कर देता है, क्योंकि document का origin अब proxy का अपना host और port है। और proxy के ज़रिए परोसे गए pages हर बार 0.1 का reCAPTCHA v3 score लेकर लौटते हैं, जिसे ज़्यादातर sites सीधे बॉट मान लेती हैं।
Native automation ने वह सब बदल दिया। TestCafe अब इसके बजाय Chromium को DevTools protocol के ज़रिए चलाता है, इसलिए रास्ते में कोई proxy नहीं है और URL rewriting बिल्कुल नहीं। यह v2.5.0 में एक प्रयोग के तौर पर आया और v3.0.0 से यही default है। अगर आपकी suite किसी मौजूदा TestCafe पर है और Chrome में चल रही है, तो यह आपके पास पहले से ही है।
तो पहला debugging step यह पुष्टि करना है कि किसी ने इसे बंद तो नहीं कर दिया। TestCafe, Firefox और Safari पर native automation को अपने आप बंद कर देता है, और disable-native-automation नाम का CLI flag तथा config file वाला उसका जुड़वाँ इसे Chromium पर भी बंद कर देते हैं। आम मामला वे suites हैं जिन्होंने सालों पहले किसी और चीज़ से बचने के लिए वह flag जोड़ा था। कोई भी solver code लिखने से पहले इसे खोजें।
# Run in Chrome, which uses native automation by default. npx testcafe chrome tests/checkout.js # This flag puts you back on the proxy and breaks reCAPTCHA. # npx testcafe chrome tests/checkout.js --disable-native-automation
एक बात साफ़ कहने लायक़ है, क्योंकि TestCafe भी यही कहता है: अगर जिस site का test हो रहा है वह आपकी अपनी है, तो सबसे अच्छा जवाब है कुछ भी हल न करना। Google एक v2 test sitekey प्रकाशित करता है जो हमेशा पास होती है, और ढीले threshold वाली एक अलग v3 key बनाना reCAPTCHA console में पाँच मिनट का बदलाव है। TestCafe की अपनी reCAPTCHA रेसिपी दोनों रास्ते विस्तार से समझाती है। हल करना उन मामलों के लिए है जहाँ वह दरवाज़ा बंद है: flow के अंदर कोई third party checkout, production keys साझा करता कोई staging environment, या ऐसा smoke test जिसे असली site पर ही चलना है।
चरण 1: page से sitekey पढ़ें
TestCafe में Selectors lazy होते हैं और retry करते हैं, इसलिए widget के render होने से पहले लिखा गया selector भी उसके दिखते ही resolve हो जाता है। test में कोई literal चिपकाने के बजाय sitekey को widget element से लें, और वही test किसी key rotation के बाद भी चलता रहेगा।
// npm install capskip
import { Selector } from 'testcafe';
const PAGE_URL = 'https://example.com/page-with-recaptcha';
fixture('Checkout').page(PAGE_URL);
test('submits behind reCAPTCHA', async t => {
const widget = Selector('.g-recaptcha');
const sitekey = await widget.getAttribute('data-sitekey');
});चरण 2: इसे test file से ही हल करें
यहीं TestCafe, browser side runners से आसान पड़ता है। आपका test function सामान्य Node है, इसलिए SDK एक सादा import है और कॉल एक सादा await। न कोई bridge बनाना है और न कोई task register करना है, जिसकी ज़रूरत Cypress से आने वाले लोग समझ बैठते हैं।
// npm install capskip
import { CapSkip } from 'capskip';
// Local mode. Change only the host to talk to a solver
// running on another machine.
const solver = new CapSkip({ host: '127.0.0.1', port: 8080 });
const result = await solver.recaptcha(sitekey, PAGE_URL);
const token = result.code;वही एक method reCAPTCHA v2, Invisible, Enterprise और v3 को कवर करता है। variants अलग कॉल नहीं, बल्कि तीसरे argument पर options हैं: invisible को 1, enterprise को 1, या version को v3 के साथ एक action string। Turnstile और GeeTest के अपने methods हैं, आकार वही। हर parameter की पूरी सूची यहाँ दी गई है: CapSkip API डॉक्यूमेंटेशन.
चरण 3: ClientFunction से token लिखें
response field एक छिपा हुआ textarea है, इसलिए सामान्य typing action उसे छूएगा तक नहीं। TestCafe के actions केवल दिखने वाले elements पर काम करते हैं, और यह जानबूझकर है। इसके बजाय एक ClientFunction आपका code page के अंदर चलाता है, जो ऐसे field के लिए सही औज़ार है जिसमें कोई असली user कभी टाइप नहीं करता।
यहाँ का जाल लगभग हर किसी को एक बार फँसाता है। ClientFunction आसपास के test के variables नहीं देख सकता। function body को serialise करके browser तक भेजा जाता है, इसलिए बाहरी scope से पकड़ा गया token run time पर एक undefined identifier बनकर पहुँचता है। इसे argument के रूप में या घोषित dependency के रूप में पास करें।
// The token is a parameter, not a closure variable.
import { ClientFunction } from 'testcafe';
const injectToken = ClientFunction(value => {
const field = document.getElementById('g-recaptcha-response');
field.value = value;
field.dispatchEvent(new Event('change', { bubbles: true }));
});
await injectToken(token);TestCafe की सलाह है कि client functions का उपयोग किसी site के व्यवहार को स्थायी रूप से बदलने के लिए न करें, और वह सलाह मानने लायक़ है। एक run के लिए एक form field में एक value लिखना वह बात नहीं है। आप page के व्यवहार को patch करने के बजाय एक field भर रहे हैं, और run खत्म होते ही वह value चली जाती है।
कुछ forms textarea पढ़ने के बजाय किसी callback का इंतज़ार करते हैं। अगर widget एक data-callback attribute घोषित करता है, तो उसी ClientFunction में उस function को token के साथ चलाएं और page ठीक वैसे ही आगे बढ़ता है जैसे किसी इंसान के लिए बढ़ता है।
पूरा चलने वाला उदाहरण
पूरा test। sitekey पढ़ें, हल करें, inject करें, submit करें, assert करें।
// npm install capskip
import { Selector, ClientFunction } from 'testcafe';
import { CapSkip, NetworkException } from 'capskip';
const PAGE_URL = 'https://example.com/page-with-recaptcha';
const solver = new CapSkip({ host: '127.0.0.1', port: 8080 });
const injectToken = ClientFunction(value => {
const field = document.getElementById('g-recaptcha-response');
field.value = value;
field.dispatchEvent(new Event('change', { bubbles: true }));
});
fixture('Checkout').page(PAGE_URL);
test('submits the protected form', async t => {
const sitekey = await Selector('.g-recaptcha').getAttribute('data-sitekey');
let token;
try {
token = (await solver.recaptcha(sitekey, PAGE_URL)).code;
} catch (err) {
if (err instanceof NetworkException) {
throw new Error('CapSkip is not reachable on 127.0.0.1:8080.');
}
throw err;
}
await injectToken(token);
await t.click(Selector('button[type=submit]'));
await t.expect(Selector('.thank-you').exists).ok();
});जितनी देर से हो सके उतनी देर से हल करें। token single use होता है और लगभग दो मिनट में expire हो जाता है, इसलिए तीन दूसरे tests से पहले चलने वाले किसी fixture hook में हल किया गया token, चौथे test के submit करने तक मर चुका होता है। कॉल को उसी test में रखें जिसे उसकी ज़रूरत है।
Timeouts, और वह जो सचमुच काटता है
एक reCAPTCHA हल में दसियों सेकंड लगते हैं, जो TestCafe की कई defaults से ज़्यादा है। अच्छी ख़बर यह है कि लोग जिन timeouts पर सबसे पहले जाते हैं, उनका इसमें कोई हाथ नहीं। आपका solver कॉल किसी page action के बजाय test function के अंदर एक await है, इसलिए 10 सेकंड का selector timeout और 3 सेकंड का assertion timeout उसे कभी देखते ही नहीं।
जो limit मायने रखती है वह है test execution timeout, जो तय करती है कि एक अकेला test कितनी देर चल सकता है। इसकी कोई default नहीं है, इसलिए यह तभी काटती है जब कोई इसे सेट करे। अगर आपका CI config कोई test execution timeout पास करता है, तो पक्का करें कि उसकी value में test के बाक़ी सारे काम के ऊपर एक धीमे हल के लिए भी जगह बचे। SDK की अपनी सीमा भी है: recaptchaTimeout की default 300 सेकंड है और हल उससे लंबा खिंचने पर वह TimeoutException उठाता है।
solver को कहीं और चलाना
Tests, CI पर चले जाते हैं, और CI runner आपकी मेज़ नहीं है। ऊपर दिए गए code में host string के अलावा कुछ नहीं बदलता।
CapSkip के दो connection modes हैं। Local, 127.0.0.1 पर bind होता है और सिर्फ़ उसी device को जवाब देता है, जो test लिखते समय आपको चाहिए। Server, आपके network या public IP पर bind होता है, इसलिए कोई build agent, container या VM उसी Windows machine को API के ज़रिए कॉल करता है। एक static public IP उस address को स्थिर रखता है। यह आपका अपना hardware है और दोनों modes में unmetered रहता है, इसलिए रात भर में पाँच सौ हल करने वाली suite की क़ीमत ठीक उतनी ही है जितनी पाँच हल करने वाली suite की।
// Same SDK, same call. Only the host moves.
const solver = new CapSkip({
host: process.env.CAPSKIP_HOST || '127.0.0.1',
port: 8080,
apiKey: process.env.CAPSKIP_API_KEY,
});SDK, environment से CAPSKIP_HOST, CAPSKIP_PORT और CAPSKIP_API_KEY खुद पढ़ लेता है, इसलिए कोई CI job दो variables से और बिना किसी code बदलाव के उसी test file को किसी दूर बैठे solver पर लगा सकता है। जैसे ही solver किसी network address पर सुनने लगे, key validation चालू करें, और हर runner को उसकी अपनी key दें ताकि एक को बाक़ियों को छुए बिना revoke किया जा सके। दोनों modes को विस्तार से यहाँ समझाया गया है: CapSkip सेटअप गाइड.
आम errors और उनका मतलब
| आप जो देखते हैं | कारण | फिक्स |
|---|---|---|
| Worker नहीं बन सका: script को उस origin से access नहीं किया जा सकता | hammerhead proxy रास्ते में है, इसलिए page का origin वह site नहीं है | disable-native-automation flag हटाएं और Chrome या Edge में चलाएं |
| हर v3 score 0.1 ही लौटकर आता है | वही कारण। test चाहे कुछ भी करे, proxy score को वहीं टिका देता है | वही हल। Native automation proxy को पूरी तरह हटा देता है |
| ReferenceError, जो कहता है कि token defined नहीं है | ClientFunction का body बाहरी scope के variables नहीं पढ़ सकता | token को argument के रूप में या घोषित dependency के रूप में पास करें |
| typing action, response field पर विफल हो जाता है | textarea छिपा हुआ है, और actions को दिखने वाला element चाहिए | इसके बजाय value को एक ClientFunction में सेट करें |
| form ऐसे token को ठुकरा देता है जो ठीक लगता है | इसे किसी hook में हल किया गया था, submit से कई मिनट पहले | test के अंदर हल करें, submit करने से ठीक पहले |
| NetworkException | CapSkip चल नहीं रहा, या host ग़लत है | ऐप शुरू करें, या host को सर्वर पते पर point करें |
| TimeoutException | solve recaptchaTimeout से ज़्यादा लंबा चला | उसे 300 सेकंड के डिफ़ॉल्ट से ऊपर बढ़ाएँ |
| ValidationException | ग़ायब या ख़राब sitekey या page URL | कॉल से पहले दोनों को log करें और जाँचें कि sitekey वही live वाली है |
FAQ
क्या मुझे Cypress की तरह किसी task या plugin की ज़रूरत है?
नहीं। Cypress आपका test code browser के अंदर चलाता है, इसलिए जिस भी चीज़ को Node चाहिए उसे एक bridge पार करना पड़ता है। TestCafe आपका test code शुरू से ही Node में चलाता है और browser तक सिर्फ़ ClientFunction के bodies जाते हैं, इसलिए solver कॉल एक सामान्य import है। इसी काम का Cypress वाला रूप यहाँ लिखा गया है: Cypress कैप्चा गाइड.
क्या मैं यह Firefox या Safari में कर सकता हूँ?
आप test चला सकते हैं, लेकिन widget के खुद ही गड़बड़ करने की उम्मीद रखें, क्योंकि उन browsers पर TestCafe वापस proxy पर लौट जाता है और यही वह configuration है जिसमें reCAPTCHA टिक नहीं पाता। कैप्चा वाले tests को Chrome या Edge पर रखें, और cross browser matrix को उन pages के लिए छोड़ दें जिन पर कोई widget है ही नहीं।
क्या यह Turnstile के लिए भी काम करता है?
हाँ, दो अंतरों के साथ। method recaptcha के बजाय turnstile है, और भरने वाला field cf-turnstile-response नाम का छिपा हुआ input है। पूरे challenge page के लिए data और pagedata values तथा token के साथ लौटने वाला user agent भी चाहिए, जिसकी जानकारी यहाँ है: Cloudflare Turnstile सॉल्वर पेज.
क्या कैप्चा tests हर commit पर चलने चाहिए?
आम तौर पर नहीं, और वजह लागत नहीं बल्कि रफ़्तार है। यहाँ हल unmetered है, लेकिन प्रति test दसियों सेकंड एक धीमी pull request जाँच बनती है। उन्हें tag करें और किसी nightly या pre release job पर चलाएं, और तेज़ suite को ऐसे build पर लगाए रखें जो test keys इस्तेमाल करता है।
संक्षेप में
पुष्टि करें कि native automation चालू है, क्योंकि पुराना proxy अपने आप ही reCAPTCHA को तोड़ देता है। sitekey को एक Selector से पढ़ें, solver को test file से ही कॉल करें क्योंकि वह पहले से Node है, और value को argument के रूप में पास करते हुए token को एक ClientFunction के ज़रिए लिखें। submit करने से ठीक पहले हल करें।
Node.js का बाक़ी हिस्सा यहाँ कवर किया गया है: Node.js कैप्चा सॉल्वर पेज। उस कैप्चा प्रकार से जुड़ी हर ख़ास बात यहाँ मिलती है: reCAPTCHA v2 सॉल्वर पेज। WebDriver आधारित किसी runner में यही काम यहाँ लिखा गया है: WebdriverIO गाइड.
इसे CI में जोड़ने से पहले एक आख़िरी बात। CapSkip एक ऐसा लोकल captcha सॉल्वर है जो आपके पास पहले से मौजूद hardware पर चलता है, इसलिए रात भर में एक हज़ार हल करने वाली suite की क़ीमत उतनी ही है जितनी दस हल करने वाली suite की।
