Klik WebDriver () vs JavaScript klik ()

126

Cerita:

Di sini, di StackOverflow, saya telah melihat pengguna melaporkan bahwa mereka tidak dapat mengklik elemen melalui selenium perintah "klik" WebDriver dan dapat mengatasinya dengan klik JavaScript dengan menjalankan skrip.

Contoh dengan Python:

element = driver.find_element_by_id("myid")
driver.execute_script("arguments[0].click();", element)

Contoh di WebDriverJS / Busur Derajat:

var elm = $("#myid");
browser.executeScript("arguments[0].click();", elm.getWebElement());

Pertanyaan:

Mengapa mengklik "via JavaScript" berfungsi ketika klik WebDriver biasa tidak? Kapan tepatnya ini terjadi dan apa kelemahan dari solusi ini (jika ada)?

Saya pribadi menggunakan solusi ini tanpa sepenuhnya memahami mengapa saya harus melakukannya dan masalah apa yang bisa ditimbulkannya.

alecxe
sumber

Jawaban:

150

Berlawanan dengan apa yang disarankan oleh jawaban yang saat ini diterima , tidak ada yang spesifik untuk PhantomJS dalam hal perbedaan antara meminta WebDriver melakukan klik dan melakukannya dalam JavaScript.

Perbedaan

Perbedaan mendasar antara kedua metode ini adalah umum untuk semua browser dan dapat dijelaskan dengan cukup sederhana:

  • WebDriver: Ketika WebDriver melakukan klik, ia berusaha sebaik mungkin untuk mensimulasikan apa yang terjadi ketika pengguna nyata menggunakan browser. Misalkan Anda memiliki elemen A yang merupakan tombol yang mengatakan "Klik saya" dan elemen B yang merupakan divelemen yang transparan tetapi memiliki dimensi dan zIndexdiatur sehingga sepenuhnya mencakup A. Kemudian Anda memberi tahu WebDriver untuk mengklik A. WebDriver akan mensimulasikan klik sehingga B menerima klik pertama . Mengapa? Karena B mencakup A, dan jika pengguna mencoba mengklik A, maka B akan mendapatkan acara terlebih dahulu. Apakah A akan mendapatkan event klik atau tidak tergantung pada bagaimana B menangani acara tersebut. Bagaimanapun, perilaku dengan WebDriver dalam kasus ini sama dengan ketika pengguna nyata mencoba mengklik A.

  • JavaScript: Sekarang, anggaplah Anda menggunakan JavaScript untuk melakukannya A.click(). Metode mengklik ini tidak mereproduksi apa yang sebenarnya terjadi ketika pengguna mencoba mengklik A. JavaScript mengirimkan clickacara langsung ke A, dan B tidak akan mendapatkan peristiwa apa pun.

Mengapa Klik JavaScript Berfungsi Ketika Klik WebDriver Tidak?

Seperti yang saya sebutkan di atas WebDriver akan mencoba mensimulasikan sebaik mungkin apa yang terjadi ketika pengguna nyata menggunakan browser. Faktanya adalah bahwa DOM dapat berisi elemen-elemen yang tidak dapat berinteraksi dengan pengguna, dan WebDriver tidak akan mengizinkan Anda mengklik elemen ini. Selain kasus tumpang tindih yang saya sebutkan, ini juga mensyaratkan bahwa elemen tidak terlihat tidak dapat diklik. Kasus umum yang saya lihat dalam pertanyaan Stack Overflow adalah seseorang yang mencoba berinteraksi dengan elemen GUI yang sudah ada di DOM tetapi menjadi terlihat hanya ketika beberapa elemen lain telah dimanipulasi. Ini kadang-kadang terjadi dengan menu dropdown: Anda harus terlebih dahulu mengklik tombol yang memunculkan dropdown sebelum item menu dapat dipilih. Jika seseorang mencoba mengklik item menu sebelum menu terlihat,Jika orang tersebut kemudian mencoba melakukannya dengan JavaScript, itu akan berfungsi karena acara tersebut dikirim langsung ke elemen, terlepas dari visibilitasnya.

Kapan Anda Harus Menggunakan JavaScript untuk Mengklik?

Jika Anda menggunakan Selenium untuk menguji aplikasi , jawaban saya untuk pertanyaan ini "hampir tidak pernah". Pada umumnya, tes Selenium Anda harus mereproduksi apa yang akan dilakukan pengguna dengan browser. Mengambil contoh menu drop down: tes harus mengklik tombol yang memunculkan drop down pertama, dan kemudian klik pada item menu. Jika ada masalah dengan GUI karena tombolnya tidak terlihat, atau tombol gagal menampilkan item menu, atau yang serupa, maka pengujian Anda akan gagal dan Anda akan mendeteksi bug. Jika Anda menggunakan JavaScript untuk mengklik, Anda tidak akan dapat mendeteksi bug ini melalui pengujian otomatis.

Saya mengatakan "hampir tidak pernah" karena mungkin ada pengecualian di mana masuk akal untuk menggunakan JavaScript. Mereka seharusnya sangat langka.

Jika Anda menggunakan Selenium untuk mengikis situs , maka tidaklah penting untuk mencoba mereproduksi perilaku pengguna. Jadi menggunakan JavaScript untuk mem-bypass GUI tidak terlalu menjadi masalah.

Louis
sumber
1
Jawaban yang jauh lebih baik, ini harus diterima. Jawaban di atas berbicara tentang kasus tepi khusus untuk PhantomJS, yang ini IMHO jauh lebih akurat.
Ardesco
@Ardesco pasti. Ditandai sebagai diterima. Sempurna dan jawaban yang cukup terperinci.
alecxe
Memberikan hadiah kepada Linh seperti yang direncanakan, tetapi saya akan memulai yang baru untuk berterima kasih atas jawaban luar biasa lainnya. Terima kasih.
alecxe
1
Jawaban yang sangat bagus dan mudah dipahami. Dalam pengalaman saya WebDriver memiliki banyak masalah dengan halaman AngularJS: Dengan beberapa elemen metode WebDriver suka clickatau sendKeysbekerja - tetapi tidak selalu. Tidak ada masalah penempatan atau pengecualian lain, test case tidak mengalami kemajuan lebih lanjut. Penebangan menunjukkan bahwa tindakan tersebut dilakukan. Terkadang menggunakan bantuan Action. Jika saya mengklik elemen secara manual (setelah test case berhenti), semuanya bekerja dengan baik. Jadi kami beralih ke JS mentah pada kesempatan itu. Saya belum bekerja dengan AngularJS selama 2 tahun terakhir, jadi semuanya mungkin lebih baik sekarang.
Würgspaß
1
@Alex Saya tidak punya tautan berguna. Jawaban saya berasal dari pengalaman dengan Selenium pada khususnya, dan dengan mensimulasikan peristiwa pengguna secara umum. Saya sudah mulai menggunakan Selenium 5 tahun yang lalu. Selama saya menggunakan Selenium, saya harus membaca kode Selenium untuk memperbaiki beberapa masalah dan saya telah mengajukan beberapa laporan bug tentang pengiriman acara dan membahas bug dengan pengembang Selenium. "sebaik mungkin" adalah tujuannya. Saya tentu saja menemukan (sekarang diperbaiki) bug yang mencegahnya mencapai tujuan itu. Beberapa bug mungkin tetap ada.
Louis
30

Klik yang dijalankan oleh driver mencoba mensimulasikan perilaku pengguna nyata sedekat mungkin sementara JavaScript HTMLElement.click()melakukan tindakan default untuk clickacara tersebut, bahkan jika elemen tersebut tidak dapat berinteraksi.

Perbedaannya adalah:

  • Pengemudi memastikan bahwa elemen tersebut terlihat dengan menggulirnya ke tampilan dan memeriksa apakah elemen tersebut dapat berinteraksi .

    Pengemudi akan meningkatkan kesalahan:

    • ketika elemen di atas pada koordinat klik bukanlah elemen yang ditargetkan atau turunan
    • ketika elemen tidak memiliki ukuran positif atau jika sepenuhnya transparan
    • ketika elemen adalah input atau tombol yang dinonaktifkan (atribut / properti disabledadalah true)
    • ketika elemen menonaktifkan penunjuk mouse (CSS pointer-eventsadalah none)


    JavaScript HTMLElement.click()akan selalu melakukan tindakan default atau paling tidak akan gagal jika elemen dinonaktifkan.

  • Pengemudi diharapkan untuk membawa elemen ke dalam fokus jika dapat fokus .

    JavaScript HTMLElement.click()tidak akan.

  • Pengemudi diharapkan memancarkan semua peristiwa (mousemove, mousedown, mouseup, klik, ...) seperti layaknya pengguna sungguhan.

    JavaScript HTMLElement.click()hanya memancarkan clickacara. Halaman ini mungkin bergantung pada acara tambahan ini dan mungkin berperilaku berbeda jika tidak dipancarkan.

    Ini adalah acara yang dipancarkan oleh pengemudi untuk klik dengan Chrome:

    mouseover {target:#topic, clientX:222, clientY:343, isTrusted:true, ... }
    mousemove {target:#topic, clientX:222, clientY:343, isTrusted:true, ... }
    mousedown {target:#topic, clientX:222, clientY:343, isTrusted:true, ... }
    mouseup {target:#topic, clientX:222, clientY:343, isTrusted:true, ... }
    click {target:#topic, clientX:222, clientY:343, isTrusted:true, ... }

    Dan ini adalah acara yang dipancarkan dengan injeksi JavaScript:

    click {target:#topic, clientX:0, clientY:0, isTrusted:false, ... }
  • Acara yang dipancarkan oleh JavaScript .click() tidak tepercaya dan tindakan default mungkin tidak dipanggil:

    https://developer.mozilla.org/en/docs/Web/API/Event/isTrusted
    https://googlechrome.github.io/samples/event-istrusted/index.html

    Perhatikan bahwa beberapa driver masih menghasilkan acara yang tidak dipercaya. Ini adalah kasus dengan PhantomJS pada versi 2.1.

  • Acara yang dipancarkan oleh JavaScript .click() tidak memiliki koordinat klik .

    Properti clientX, clientY, screenX, screenY, layerX, layerYdiatur ke 0. Halaman mungkin bergantung pada mereka dan mungkin berperilaku berbeda.


Mungkin ok untuk menggunakan JavaScript .click()untuk menghapus beberapa data, tetapi itu tidak dalam konteks pengujian. Itu mengalahkan tujuan pengujian karena tidak mensimulasikan perilaku pengguna. Jadi, jika klik dari driver gagal, maka pengguna nyata kemungkinan besar juga gagal melakukan klik yang sama dalam kondisi yang sama.


Apa yang membuat pengemudi gagal mengklik elemen ketika kami mengharapkannya berhasil?

  • Elemen yang ditargetkan belum terlihat / berinteraksi karena penundaan atau efek transisi.

    Beberapa contoh :

    https://developer.mozilla.org/fr/docs/Web (menu navigasi dropdown) http://materializecss.com/side-nav.html (bilah sisi dropdown)

    Workarrounds:

    Tambahkan pelayan untuk menunggu visibilitas, ukuran minimum atau posisi tetap:

    // wait visible
    browser.wait(ExpectedConditions.visibilityOf(elem), 5000);
    
    // wait visible and not disabled
    browser.wait(ExpectedConditions.elementToBeClickable(elem), 5000);
    
    // wait for minimum width
    browser.wait(function minimumWidth() {
        return elem.getSize().then(size => size.width > 50);
    }, 5000);

    Coba lagi untuk mengklik sampai berhasil:

    browser.wait(function clickSuccessful() {
        return elem.click().then(() => true, (ex) => false);
    }, 5000);

    Tambahkan penundaan yang cocok dengan durasi animasi / transisi:

    browser.sleep(250);


  • Elemen yang ditargetkan berakhir dengan elemen mengambang setelah digulir ke tampilan:

    Pengemudi secara otomatis menggulir elemen ke tampilan untuk membuatnya terlihat. Jika halaman berisi elemen mengambang / lengket (menu, iklan, footer, notifikasi, kebijakan cookie ..), elemen tersebut mungkin akan tertutup dan tidak lagi terlihat / berinteraksi.

    Contoh: https://twitter.com/?lang=en

    Penanganan masalah:

    Atur ukuran jendela ke yang lebih besar untuk menghindari pengguliran atau elemen mengambang.

    Pindahkan elemen dengan Yoffset negatif lalu klik:

      browser.actions()
         .mouseMove(elem, {x: 0, y: -250})
         .click()
         .perform();

    Gulir elemen ke tengah jendela sebelum klik:

    browser.executeScript(function scrollCenter(elem) {
      var win = elem.ownerDocument.defaultView || window,
        box = elem.getBoundingClientRect(),
        dy = box.top - (win.innerHeight - box.height) / 2;
      win.scrollTo(win.pageXOffset, win.pageYOffset + dy);
    }, element);
    
    element.click();

    Sembunyikan elemen apung jika tidak dapat dihindari:

    browser.executeScript(function scrollCenter(elem) {
      elem.style.display = 'none';
    }, element);
Florent B.
sumber
17

CATATAN: sebut saja 'klik' adalah klik pengguna akhir. 'js click' adalah klik melalui JS

Mengapa mengklik "via JavaScript" berfungsi ketika klik WebDriver biasa tidak?

Ada 2 kasus untuk ini terjadi:

I. Jika Anda menggunakan PhamtomJS

Maka ini adalah perilaku yang paling umum diketahui PhantomJS. Beberapa elemen terkadang tidak dapat diklik, misalnya <div>. Ini karena PhantomJSini asli dibuat untuk mensimulasikan mesin browser (seperti HTML + CSS awal -> komputasi CSS -> rendering). Tetapi itu tidak berarti berinteraksi dengan cara pengguna akhir (melihat, mengklik, menyeret). Karena PhamtomJSitu hanya didukung sebagian dengan interaksi pengguna akhir.

MENGAPA JS KLIK BEKERJA? Adapun klik, mereka semua adalah klik berarti. Itu seperti pistol dengan 1 barel dan 2 pemicu . Satu dari viewport, satu dari JS. Karena PhamtomJShebat dalam mensimulasikan mesin peramban, klik JS seharusnya bekerja dengan sempurna.

II Penangan acara "klik" harus terikat dalam periode waktu yang buruk.

Sebagai contoh, kami mendapat <div>

  • -> Kami melakukan beberapa perhitungan

  • -> lalu kita ikat acara klik ke <div>.

  • -> Plus dengan beberapa pengkodean sudut yang buruk (mis. Tidak menangani siklus scope dengan benar)

Kita mungkin berakhir dengan hasil yang sama. Klik tidak akan berfungsi, karena WebdriverJS mencoba mengklik pada elemen ketika tidak memiliki pengendali event klik.

MENGAPA JS KLIK BEKERJA? Js klik seperti menyuntikkan js langsung ke browser. Mungkin dengan 2 cara,

Fist melalui konsol devtools (ya, WebdriverJS berkomunikasi dengan konsol devtools).

Kedua adalah menyuntikkan <script>tag langsung ke HTML.

Untuk setiap browser, perilakunya akan berbeda. Tetapi bagaimanapun juga, metode ini lebih menyulitkan daripada mengklik tombol. Klik menggunakan apa yang sudah ada (klik pengguna akhir), klik saja akan melalui backdoor.

Dan untuk klik js akan tampak sebagai tugas yang tidak sinkron. Hal ini terkait dengan topik yang agak rumit tentang ' tugas asinkron peramban dan penjadwalan tugas CPU ' (baca beberapa waktu lalu tidak dapat menemukan artikel lagi). Singkatnya, ini sebagian besar akan menghasilkan js klik akan perlu menunggu siklus penjadwalan tugas CPU dan akan berjalan sedikit lebih lambat setelah pengikatan acara klik. (Anda bisa mengetahui kasus ini ketika Anda menemukan elemen kadang-kadang dapat diklik, kadang tidak.)

Kapan tepatnya ini terjadi dan apa kelemahan dari solusi ini (jika ada)?

=> Seperti disebutkan di atas, keduanya berarti untuk satu tujuan, tetapi tentang menggunakan pintu masuk mana:

  • Klik: menggunakan apa yang disediakan oleh browser default.
  • Klik JS: melalui backdoor.

=> Untuk kinerja, sulit untuk mengatakan karena itu bergantung pada browser. Tetapi umumnya:

  • Klik: tidak berarti lebih cepat tetapi hanya menandatangani posisi yang lebih tinggi dalam daftar jadwal tugas eksekusi CPU.
  • Klik JS: tidak berarti lebih lambat tetapi hanya masuk ke posisi terakhir dari daftar jadwal tugas CPU.

=> Kerugian:

  • Klik: tampaknya tidak memiliki downside kecuali Anda menggunakan PhamtomJS.
  • Klik JS: sangat buruk untuk kesehatan. Anda mungkin secara tidak sengaja mengklik sesuatu yang tidak ada pada tampilan. Saat Anda menggunakan ini, pastikan elemennya ada dan tersedia untuk dilihat dan diklik sebagai sudut pandang pengguna akhir.

PS jika Anda mencari solusi.

  • Menggunakan PhantomJS? Saya akan menyarankan menggunakan Chrome tanpa kepala. Ya, Anda dapat menyiapkan Chrome tanpa kepala di Ubuntu. Hal berjalan seperti Chrome tetapi hanya tidak memiliki tampilan dan kurang buggy seperti PhantomJS.
  • Tidak menggunakan PhamtomJS tetapi masih mengalami masalah? Saya akan menyarankan menggunakan ExpectedCondition of Protractor with browser.wait()( periksa ini untuk informasi lebih lanjut )

(Saya ingin membuatnya singkat, tetapi berakhir dengan buruk. Apa pun yang berhubungan dengan teori rumit untuk dijelaskan ...)

Linh Pham
sumber