Cara Memecahkan CAPTCHA dalam Pengujian WebdriverIO dengan Node.js

Langkah CAPTCHA di WebdriverIO adalah panggilan Node.js, bukan panggilan browser. Anda memecahkan CAPTCHA di dalam proses pengujian, menyerahkan token ke halaman lewat browser.execute, lalu submit formulirnya. Ada dua hal yang sering menjebak orang, dan keduanya tidak ada hubungannya dengan solver. Letakkan proses pemecahan di dalam browser context dan sama sekali tidak ada cara baginya untuk menjangkau komputer Anda. Biarkan Mocha memakai timeout bawaan tiga puluh detik, dan pengujian akan mati di tengah proses pemecahan dengan pesan yang tidak menjelaskan apa-apa. Berikut ini custom command-nya, satu perubahan konfigurasi, dan spec yang berhasil.
Apa yang Anda butuhkan
- WebdriverIO 8 atau yang lebih baru dengan framework Mocha. Semua yang ada di bawah ini bersifat async, karena mode synchronous lama sudah tidak ada lagi.
- Node.js 18 atau yang lebih baru, dan paket CapSkip yang sudah terpasang di proyek pengujian.
- Halaman yang benar-benar menyajikan challenge. Sitekey pengujian yang selalu lolos tidak akan menguji apa pun dari ini.
- CapSkip yang berjalan dalam Local mode jika pengujian berjalan di komputer Anda sendiri, atau dalam Server mode jika berjalan di CI runner atau grid. Keduanya dijelaskan di pengaturan koneksi.
# Install into the project that runs wdio, not into the browser image. npm install capskip
Pecahkan di Node, bukan di browser
Ini adalah kesalahan yang sebaiknya disingkirkan lebih dulu, karena WebdriverIO membuatnya mudah terjadi. Perintah browser.execute men-serialize fungsi Anda, mengirimkannya ke browser, dan menjalankannya di dalam halaman. Di sana tidak ada module loader, jadi SDK-nya sama sekali tidak tersedia. Bahkan seandainya ada, halaman itu sendiri adalah yang sedang Anda uji, dan menyerahkan alamat solver Anda kepadanya bukan hal yang ingin Anda lakukan.
Alasan yang lebih kuat adalah routing. Begitu browser berada di mana pun selain komputer Anda sendiri, dan di grid atau penyedia cloud device tidak pernah demikian, alamat loopback di dalam browser itu adalah milik host browser tersebut. Solver Anda tidak ada di sana. Proses Node yang menjalankan wdio adalah yang tahu cara menjangkau CapSkip, jadi proses pemecahan tetap terjadi di sana dan hanya token yang sudah jadi yang masuk ke halaman.
Langkah 1: daftarkan custom command
Daripada meng-import SDK ke setiap spec, tambahkan satu command di before hook konfigurasi Anda. Command itu akan tersedia pada objek browser di setiap pengujian, dan alamat solver hanya ada di satu tempat.
// npm install capskip
const { CapSkip } = require('capskip');
exports.config = {
framework: 'mocha',
before: function () {
const solver = new CapSkip({
host: process.env.CAPSKIP_HOST || '127.0.0.1',
port: Number(process.env.CAPSKIP_PORT || 8080),
});
browser.addCommand('solveRecaptcha', async function (sitekey) {
const result = await solver.recaptcha(sitekey, await this.getUrl());
return result.code; // the token, ready to inject
});
},
};Di dalam addCommand, nilai this adalah browser scope, itulah sebabnya getUrl berfungsi di sana. Detail ini benar-benar berguna: API membutuhkan URL halaman tempat widget berada, dan meminta browser untuk memberikannya berarti spec tidak perlu mengulang URL yang sudah dinavigasinya.
Baca host dari environment, bukan meng-hardcode-nya. Suite yang sama kemudian bisa berjalan melawan solver di laptop Anda maupun solver bersama dari CI tanpa perubahan kode.
Langkah 2: suntikkan token dan submit
Google menaruh jawabannya di dalam textarea tersembunyi dengan id g-recaptcha-response. Karena tersembunyi, setValue sebanyak apa pun tidak akan menyentuhnya. Set value-nya secara langsung, lalu submit formulirnya seperti yang akan dilakukan manusia.
const token = await browser.solveRecaptcha('YOUR_SITEKEY');
// Put the token where the page already expects to find it.
await browser.execute((value) => {
document.getElementById('g-recaptcha-response').value = value;
}, token);
await $('button[type="submit"]').click();Itu sudah mencakup kasus umum, yaitu formulir yang membaca textarea saat Anda menekan submit. Beberapa halaman justru mendeklarasikan data-callback pada widget dan sama sekali tidak pernah melihat textarea-nya. Jika itu yang sedang Anda uji, panggil callback-nya dari blok browser.execute yang sama setelah men-set value, karena mengisi kolom yang tidak pernah dibaca siapa pun tidak mengubah apa-apa.
Langkah 3: naikkan timeout Mocha
WebdriverIO menetapkan Mocha ke timeout bawaan 30000 milidetik, yang cukup longgar untuk mengklik sesuatu tetapi jauh terlalu singkat untuk memecahkan sebuah challenge. Pengujian yang seharusnya lolos jadi gagal di sini, dan kegagalannya menyebut nama Mocha, bukan apa pun yang berkaitan dengan proses pemecahan, yang membuat orang mencari-cari di tempat yang salah selama berjam-jam.
exports.config = {
framework: 'mocha',
mochaOpts: {
// The 30000 default expires mid solve. Give it room.
timeout: 120000,
},
};Atur kedua batas waktu ini dalam urutan yang benar. SDK menyerah setelah recaptchaTimeout, yang defaultnya 300 detik, dan Mocha menyerah setelah timeout-nya sendiri. Jika SDK Anda buat lebih rendah dari Mocha, Anda akan mendapat TimeoutException yang menyebut proses pemecahan. Jika dibuat lebih tinggi, Mocha akan mematikan pengujian lebih dulu, dan yang Anda ketahui hanya bahwa sesuatu memakan waktu terlalu lama. Timeout solver 90 detik di bawah timeout Mocha 120000 milidetik adalah pasangan yang masuk akal untuk suite yang harus tetap cepat.
Menjalankannya di CI atau grid
Pastikan dulu komputer mana yang perlu menjangkau solver, karena bukan yang paling jelas terlihat. Browser tidak pernah berbicara langsung dengan CapSkip. Proses yang menjalankan wdio-lah yang melakukannya. Jadi pada GitHub Actions runner, container, atau laptop yang mengendalikan cloud browser, pemanggilnya adalah runner itu, dan mengarahkannya ke alamat loopback-nya sendiri tidak akan menemukan apa-apa.
| Mode | Mendengarkan di | Gunakan saat |
|---|---|---|
| Lokal | 127.0.0.1, hanya perangkat itu | Anda menjalankan wdio di komputer yang sama dengan solver |
| Server | Alamat jaringan atau IP publik Anda | CI runner, container, suite bersama, grid |
Server mode mencakup semua yang ada di baris kedua. Ganti listen address di aplikasi, set CAPSKIP_HOST pada runner, dan setiap job akan berbagi satu solver. IP publik statis layak dimiliki kalau pemanggilnya berada di luar jaringan Anda. Tidak ada satu pun dari ini yang mengubah hakikat produknya: tetap perangkat keras milik Anda dan tetap tanpa meteran biaya, jadi berpindah dari alamat loopback hanya memindahkan tempat solver itu berjalan, tidak ada yang lain. CapSkip adalah aplikasi Windows, jadi dalam praktiknya itu berarti satu mesin Windows yang dipanggil oleh runner Anda.
Spec lengkapnya
describe('protected signup form', () => {
it('submits with a solved challenge', async () => {
await browser.url('https://example.com/page-with-recaptcha');
await $('#email').setValue('[email protected]');
// Solve here, submit two lines later. The token is short lived.
const token = await browser.solveRecaptcha('YOUR_SITEKEY');
await browser.execute((value) => {
document.getElementById('g-recaptcha-response').value = value;
}, token);
await $('button[type="submit"]').click();
await expect($('.signup-success')).toBeDisplayed();
});
});Perhatikan betapa sedikit dari itu yang sebenarnya berkaitan dengan CAPTCHA. Tiga baris menanggung seluruh integrasinya, dan sisanya adalah pengujian yang memang akan Anda tulis. Jaga agar proses pemecahan dan submit berada di dalam body pengujian yang sama, sehingga token masih berumur beberapa detik saat dibaca formulir.
Kesalahan umum
| Apa yang Anda lihat | Penyebab | Perbaiki |
|---|---|---|
| Timeout Mocha 30000ms terlampaui | Proses pemecahan berlangsung lebih lama dari default | Naikkan mochaOpts.timeout dan turunkan recaptchaTimeout di bawahnya |
| solver tidak terdefinisi di dalam browser.execute | Fungsinya berjalan di halaman, bukan di Node | Pecahkan sebelum pemanggilan execute, lalu masukkan hanya token-nya |
| NetworkException dari CI, normal saat lokal | Runner tidak bisa menjangkau solver | Beralihlah ke Server mode dan set CAPSKIP_HOST pada runner |
| Formulir menolak token yang berhasil dipecahkan dengan baik | Halaman menggunakan callback dan mengabaikan textarea | Panggil callback widget setelah men-set value-nya |
| ERROR_GOOGLEKEY | Sitekey Turnstile dikirim ke metode reCAPTCHA | Gunakan metode turnstile untuk widget Turnstile |
Daftar lengkap kode dan penyebab masing-masing kode ada di dokumentasi API CapSkip.
FAQ
Bisakah saya memanggil solver di dalam browser.execute?
Tidak, dan ini gagal karena dua alasan yang terpisah. Fungsi yang Anda kirim di-serialize dan dijalankan di dalam halaman, tempat tidak ada module loader maupun SDK. Bahkan seandainya ada, browser sering kali berada di host yang sama sekali berbeda, jadi alamat yang akan Anda hubungi bukan milik Anda. Pecahkan di proses Node dan masukkan token yang sudah jadi sebagai argumen.
Apakah ini bekerja dengan remote grid atau cloud browser?
Ya, dan setup semacam itulah alasan proses pemecahan harus dilakukan di Node. Proses pengujian Anda berjalan secara lokal atau di runner dan menjangkau solver secara langsung, sementara browser berada di tempat lain dan hanya pernah menerima token. Jalankan CapSkip dalam Server mode agar mesin mana pun yang menjalankan suite bisa menjangkaunya, dan tidak ada yang berubah pada spec-nya.
Haruskah saya memecahkan sekali di before hook lalu memakai ulang token-nya?
Tidak. Token reCAPTCHA hanya sekali pakai dan tetap valid selama kurang lebih dua menit, jadi pengujian kedua yang memakainya akan ditolak, begitu juga pengujian pertama yang berjalan setelah spec yang lambat. Pecahkan di dalam setiap pengujian yang membutuhkannya. Solver bersama bisa menangani panggilan tambahan tanpa biaya per pemecahan, jadi tidak ada yang dihemat dengan menimbun token.
Saya memakai Cucumber atau Jasmine, bukan Mocha. Apa yang berubah?
Hanya nama pengaturan timeout-nya. Custom command, proses injeksi, dan pertanyaan soal Server mode semuanya identik. Naikkan cucumberOpts.timeout atau jasmineOpts.defaultTimeoutInterval, bukan mochaOpts.timeout, dan jaga agar timeout solver tetap berada di bawah mana pun yang Anda atur.
Versi singkatnya
Daftarkan satu custom command di before hook, naikkan timeout framework di atas waktu pemecahan, dan suntikkan token dengan browser.execute alih-alih mencoba mengetiknya. Untuk sisi client, lihat halaman pemecah CAPTCHA Node.js, untuk sisi WebDriver lihat halaman pemecah CAPTCHA Selenium, dan untuk mengetahui apa sebenarnya token itu, lihat halaman pemecah reCAPTCHA v2. Test suite adalah tempat penetapan harga per pemecahan terasa paling menyakitkan, karena suite yang berjalan pada setiap pull request memecahkan formulir yang sama ratusan kali seminggu. Itulah yang berubah dengan kepemilikan atas pemecah captcha tanpa batas yang kapasitasnya di perangkat keras yang sudah Anda miliki tetap sama biayanya, baik CI berjalan dua kali sehari maupun dua kali sejam.
