WebDriver click () เทียบกับคลิก JavaScript ()


127

เรื่องราว:

ที่นี่ใน StackOverflow ฉันเคยเห็นผู้ใช้รายงานว่าไม่สามารถคลิกองค์ประกอบผ่านคำสั่ง "click" ของ selenium WebDriver และสามารถแก้ไขได้ด้วยการคลิก JavaScript โดยเรียกใช้สคริปต์

ตัวอย่างใน Python:

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

ตัวอย่างใน WebDriverJS / Protractor:

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

คำถาม:

เหตุใดการคลิก "ผ่าน JavaScript" จึงทำงานเมื่อคลิก WebDriver ปกติไม่ได้ สิ่งนี้เกิดขึ้นเมื่อใดและข้อเสียของวิธีแก้ปัญหานี้คืออะไร (ถ้ามี)

ฉันใช้วิธีแก้ปัญหานี้เป็นการส่วนตัวโดยไม่เข้าใจว่าทำไมฉันต้องทำและปัญหาที่อาจนำไปสู่

คำตอบ:


151

ตรงกันข้ามกับคำตอบที่ได้รับการยอมรับในปัจจุบันไม่มีอะไรเฉพาะเจาะจงสำหรับ PhantomJS เมื่อพูดถึงความแตกต่างระหว่างการให้ WebDriver ทำการคลิกและการทำใน JavaScript

ความแตกต่าง

ความแตกต่างที่สำคัญระหว่างสองวิธีนี้เป็นเรื่องปกติสำหรับเบราว์เซอร์ทั้งหมดและสามารถอธิบายได้ค่อนข้างง่าย:

  • WebDriver: เมื่อ WebDriver ทำการคลิกโปรแกรมจะพยายามอย่างดีที่สุดเท่าที่จะทำได้เพื่อจำลองสิ่งที่เกิดขึ้นเมื่อผู้ใช้จริงใช้เบราว์เซอร์ สมมติว่าคุณมีองค์ประกอบ A ซึ่งเป็นปุ่มที่ระบุว่า "คลิกฉัน" และองค์ประกอบ B ซึ่งเป็นdivองค์ประกอบที่โปร่งใส แต่มีขนาดและzIndexตั้งค่าเพื่อให้ครอบคลุม A อย่างสมบูรณ์จากนั้นคุณบอก WebDriver ให้คลิก A. WebDriver จะ จำลองคลิกเพื่อให้ B ได้รับการคลิกครั้งแรก ทำไม? เนื่องจาก B ครอบคลุม A และหากผู้ใช้พยายามคลิกที่ A ดังนั้น B จะได้รับเหตุการณ์ก่อน ในที่สุด A จะได้รับเหตุการณ์คลิกหรือไม่นั้นขึ้นอยู่กับว่า B จัดการกับเหตุการณ์อย่างไร ไม่ว่าในกรณีใดพฤติกรรมของ WebDriver ในกรณีนี้จะเหมือนกับเมื่อผู้ใช้จริงพยายามคลิกที่ A

  • JavaScript: ตอนนี้สมมติว่าคุณใช้ JavaScript A.click()เพื่อทำ วิธีการคลิกนี้ไม่ได้จำลองสิ่งที่เกิดขึ้นจริงเมื่อผู้ใช้พยายามคลิก A JavaScript ส่งclickเหตุการณ์ไปยัง A โดยตรงและ B จะไม่ได้รับเหตุการณ์ใด ๆ

เหตุใด JavaScript Click จึงทำงานได้เมื่อ WebDriver คลิกไม่ได้?

ดังที่ฉันได้กล่าวไว้ข้างต้น WebDriver จะพยายามจำลองให้ดีที่สุดว่าจะเกิดอะไรขึ้นเมื่อผู้ใช้จริงใช้เบราว์เซอร์ ข้อเท็จจริงของเรื่องนี้ก็คือ DOM สามารถมีองค์ประกอบที่ผู้ใช้ไม่สามารถโต้ตอบได้และ WebDriver จะไม่อนุญาตให้คุณคลิกที่องค์ประกอบเหล่านี้ นอกจากกรณีที่ซ้อนทับที่ฉันพูดถึงแล้วสิ่งนี้ยังรวมถึงองค์ประกอบที่มองไม่เห็นไม่สามารถคลิกได้ กรณีทั่วไปที่ฉันเห็นในคำถาม Stack Overflow คือคนที่พยายามโต้ตอบกับองค์ประกอบ GUI ที่มีอยู่แล้วใน DOM แต่จะมองเห็นได้ก็ต่อเมื่อมีการจัดการองค์ประกอบอื่น ๆ บางครั้งสิ่งนี้เกิดขึ้นกับเมนูแบบเลื่อนลง: คุณต้องคลิกที่ปุ่มก่อนเพื่อให้เมนูแบบเลื่อนลงปรากฏขึ้นก่อนจึงจะสามารถเลือกรายการเมนูได้ หากมีคนพยายามคลิกรายการเมนูก่อนที่เมนูจะปรากฏขึ้นหากบุคคลนั้นพยายามทำด้วย JavaScript มันจะทำงานได้เนื่องจากเหตุการณ์ถูกส่งตรงไปยังองค์ประกอบโดยไม่คำนึงถึงการมองเห็น

คุณควรใช้ JavaScript ในการคลิกเมื่อใด

หากคุณใช้ซีลีเนียมในการทดสอบแอปพลิเคชันคำตอบของฉันสำหรับคำถามนี้คือ"แทบไม่เคย" โดยทั่วไปแล้วการทดสอบซีลีเนียมของคุณควรจำลองสิ่งที่ผู้ใช้ทำกับเบราว์เซอร์ จากตัวอย่างเมนูแบบเลื่อนลง: การทดสอบควรคลิกที่ปุ่มที่แสดงรายการแบบเลื่อนลงก่อนจากนั้นคลิกที่รายการเมนู หากมีปัญหากับ GUI เนื่องจากปุ่มมองไม่เห็นหรือปุ่มไม่แสดงรายการเมนูหรือสิ่งที่คล้ายกันการทดสอบของคุณจะล้มเหลวและคุณจะตรวจพบข้อบกพร่อง หากคุณใช้ JavaScript เพื่อคลิกไปรอบ ๆ คุณจะไม่สามารถตรวจพบข้อบกพร่องเหล่านี้ผ่านการทดสอบอัตโนมัติ

ฉันพูดว่า "แทบจะไม่เคย" เพราะอาจมีข้อยกเว้นที่เหมาะสมในการใช้ JavaScript พวกมันน่าจะหายากมาก

หากคุณกำลังใช้ซีลีเนียมในการคัดลอกไซต์การพยายามทำให้เกิดพฤติกรรมของผู้ใช้ไม่สำคัญเท่า ดังนั้นการใช้ JavaScript เพื่อข้าม GUI จึงเป็นปัญหาน้อยกว่า


1
คำตอบที่ดีกว่านี้ควรเป็นคำตอบที่ได้รับการยอมรับ คำตอบข้างต้นกำลังพูดถึงเคสขอบเฉพาะสำหรับ PhantomJS อันนี้เป็น IMHO ที่แม่นยำกว่ามาก
Ardesco

@Ardesco แน่นอน. ทำเครื่องหมายว่ายอมรับแล้ว สมบูรณ์แบบและเป็นคำตอบที่ค่อนข้างละเอียด
alecxe

มอบรางวัลให้ Linh ตามแผนที่วางไว้ แต่ฉันจะเริ่มต้นใหม่เพื่อขอบคุณสำหรับคำตอบที่ยอดเยี่ยมอีกครั้ง ขอบคุณ
alecxe

1
คำตอบที่ดีและเข้าใจง่าย จากประสบการณ์ของฉัน WebDriver มีปัญหามากมายกับเพจ AngularJS: ด้วยองค์ประกอบบางอย่างที่วิธี WebDriver เหมือนclickหรือใช้sendKeysงานได้ - แต่ก็ไม่เสมอไป ไม่มีปัญหาในการระบุตำแหน่งหรือข้อยกเว้นอื่น ๆ กรณีทดสอบไม่คืบหน้าใด ๆ การบันทึกแสดงให้เห็นว่ามีการดำเนินการ บางครั้งก็ใช้ตัวActionช่วย หากฉันคลิกที่องค์ประกอบด้วยตนเอง (หลังจากหยุดกรณีทดสอบ) ทุกอย่างก็ใช้ได้ดี เราจึงเปลี่ยนไปใช้ JS ดิบในครั้งนั้น ฉันไม่ได้ทำงานกับ AngularJS ในช่วง 2 ปีที่ผ่านมาดังนั้นสิ่งต่างๆอาจจะดีขึ้นในตอนนี้
Würgspaß

1
@ อเล็กซ์ฉันไม่มีลิงค์ที่มีประโยชน์ คำตอบของฉันได้มาจากประสบการณ์กับซีลีเนียมโดยเฉพาะและจากการจำลองเหตุการณ์ของผู้ใช้โดยทั่วไป ฉันเริ่มใช้ซีลีเนียมเมื่อ 5 ปีที่แล้ว ในช่วงเวลาที่ฉันใช้ Selenium ฉันต้องอ่านรหัสของ Selenium เพื่อแก้ไขปัญหาบางอย่างและฉันได้ยื่นรายงานข้อผิดพลาดเกี่ยวกับการจัดส่งเหตุการณ์และพูดคุยถึงข้อบกพร่องกับผู้พัฒนา Selenium "ดีที่สุดเท่าที่จะทำได้" คือเป้าหมาย ฉันพบข้อบกพร่อง (แก้ไขแล้ว) ที่ทำให้ไม่สามารถบรรลุเป้าหมายนั้นได้ ข้อบกพร่องบางอย่างอาจยังคงอยู่
Louis

30

การคลิกที่เรียกใช้โดยโปรแกรมควบคุมจะพยายามจำลองพฤติกรรมของผู้ใช้จริงให้ใกล้เคียงที่สุดในขณะที่ JavaScript HTMLElement.click()ดำเนินการดำเนินการเริ่มต้นสำหรับclickเหตุการณ์แม้ว่าองค์ประกอบนั้นจะไม่สามารถโต้ตอบได้

ความแตกต่างคือ:

  • คนขับรถเพื่อให้แน่ใจว่าองค์ประกอบสามารถมองเห็นได้โดยการเลื่อนลงในมุมมองและการตรวจสอบว่าองค์ประกอบคือinteractable

    ไดรเวอร์จะแจ้งข้อผิดพลาด:

    • เมื่อองค์ประกอบที่อยู่ด้านบนที่พิกัดของการคลิกไม่ใช่องค์ประกอบเป้าหมายหรือลูกหลาน
    • เมื่อองค์ประกอบไม่มีขนาดบวกหรือหากมีความโปร่งใสเต็มที่
    • เมื่อองค์ประกอบเป็นอินพุตหรือปุ่มปิดใช้งาน (แอตทริบิวต์ / คุณสมบัติdisabledคือtrue)
    • เมื่อองค์ประกอบปิดใช้งานตัวชี้เมาส์ (CSS pointer-eventsคือnone)


    JavaScript HTMLElement.click()จะดำเนินการตามค่าเริ่มต้นเสมอหรือจะล้มเหลวอย่างดีที่สุดหากองค์ประกอบถูกปิดใช้งาน

  • คาดว่าคนขับจะนำองค์ประกอบเข้าสู่โฟกัสหากสามารถโฟกัสได้

    JavaScript HTMLElement.click()จะไม่

  • คาดว่าไดรเวอร์จะปล่อยเหตุการณ์ทั้งหมด (mousemove, mousedown, mouseup, click, ... ) เหมือนกับผู้ใช้จริง

    JavaScript HTMLElement.click()ส่งเฉพาะclickเหตุการณ์ หน้านี้อาจอาศัยเหตุการณ์พิเศษเหล่านี้และอาจทำงานแตกต่างไปจากนี้หากไม่มีการแสดง

    นี่คือเหตุการณ์ที่ไดรเวอร์ปล่อยออกมาสำหรับการคลิกด้วย 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, ... }

    และนี่คือเหตุการณ์ที่เกิดจากการฉีด JavaScript:

    click {target:#topic, clientX:0, clientY:0, isTrusted:false, ... }
  • เหตุการณ์ที่ปล่อยออกมาโดย JavaScript .click() ไม่น่าเชื่อถือและไม่สามารถเรียกใช้การดำเนินการเริ่มต้นได้:

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

    โปรดทราบว่าไดรเวอร์บางตัวยังคงสร้างเหตุการณ์ที่ไม่น่าเชื่อถือ นี่เป็นกรณีของ PhantomJS ในเวอร์ชัน 2.1

  • กรณีที่ปล่อยออกมาจาก JavaScript ที่ไม่ได้มีพิกัดในการคลิก.click()

    คุณสมบัติมีการกำหนดให้clientX, clientY, screenX, screenY, layerX, layerY 0หน้าอาจต้องพึ่งพาและอาจทำงานแตกต่างออกไป


อาจเป็นเรื่องปกติที่จะใช้ JavaScript .click()เพื่อคัดลอกข้อมูลบางส่วน แต่ไม่ได้อยู่ในบริบทการทดสอบ มันเอาชนะจุดประสงค์ของการทดสอบเนื่องจากไม่ได้จำลองพฤติกรรมของผู้ใช้ ดังนั้นหากการคลิกจากไดรเวอร์ล้มเหลวผู้ใช้จริงมักจะล้มเหลวในการคลิกเดียวกันในเงื่อนไขเดียวกัน


อะไรทำให้คนขับไม่สามารถคลิกองค์ประกอบเมื่อเราคาดหวังว่าจะสำเร็จ

  • องค์ประกอบเป้าหมายยังมองไม่เห็น / โต้ตอบได้เนื่องจากความล่าช้าหรือผลการเปลี่ยนแปลง

    ตัวอย่างบางส่วน:

    https://developer.mozilla.org/fr/docs/Web (เมนูการนำทางแบบเลื่อนลง) http://materializecss.com/side-nav.html (แถบด้านข้างแบบเลื่อนลง)

    Workarrounds:

    เพิ่มพนักงานเสิร์ฟเพื่อรอการมองเห็นขนาดขั้นต่ำหรือตำแหน่งที่มั่นคง:

    // 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);

    ลองคลิกอีกครั้งจนกว่าจะสำเร็จ:

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

    เพิ่มการหน่วงเวลาที่ตรงกับระยะเวลาของภาพเคลื่อนไหว / การเปลี่ยนแปลง:

    browser.sleep(250);


  • องค์ประกอบเป้าหมายจะจบลงด้วยองค์ประกอบลอยเมื่อเลื่อนเข้าไปในมุมมอง:

    โปรแกรมควบคุมจะเลื่อนองค์ประกอบในมุมมองโดยอัตโนมัติเพื่อให้มองเห็นได้ หากหน้าเว็บมีองค์ประกอบลอย / ติดหนึบ (เมนู, โฆษณา, ส่วนท้าย, การแจ้งเตือน, นโยบายคุกกี้ .. ) องค์ประกอบนั้นอาจครอบคลุมและจะไม่สามารถมองเห็น / โต้ตอบได้อีกต่อไป

    ตัวอย่าง: https://twitter.com/?lang=th

    วิธีการแก้ปัญหา:

    กำหนดขนาดของหน้าต่างให้ใหญ่ขึ้นเพื่อหลีกเลี่ยงการเลื่อนหรือองค์ประกอบลอย

    เลื่อนYเมาส์ไปเหนือองค์ประกอบด้วยค่าชดเชยเชิงลบจากนั้นคลิก:

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

    เลื่อนองค์ประกอบไปที่กึ่งกลางของหน้าต่างก่อนคลิก:

    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();

    ซ่อนองค์ประกอบลอยหากไม่สามารถหลีกเลี่ยงได้:

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

17

หมายเหตุ: เรียกว่า 'คลิก' คือคลิกของผู้ใช้ปลายทาง "js click" คือคลิกผ่าน JS

เหตุใดการคลิก "ผ่าน JavaScript" จึงทำงานเมื่อคลิก WebDriver ปกติไม่ได้

มี 2 ​​กรณีที่จะเกิดขึ้น:

I. หากคุณใช้ PhamtomJS

PhantomJSแล้วนี้เป็นพฤติกรรมที่รู้จักมากที่สุดร่วมกันของ <div>องค์ประกอบบางอย่างบางครั้งก็ไม่สามารถคลิกได้ยกตัวอย่างเช่น เนื่องจากPhantomJSเป็นต้นฉบับที่สร้างขึ้นเพื่อจำลองกลไกของเบราว์เซอร์ (เช่น HTML + CSS เริ่มต้น -> ประมวลผล CSS -> การแสดงผล) แต่ไม่ได้หมายความว่าจะโต้ตอบกับผู้ใช้ปลายทาง (การดูการคลิกการลาก) ดังนั้นจึงPhamtomJSรองรับการโต้ตอบกับผู้ใช้ปลายทางเพียงบางส่วนเท่านั้น

เหตุใด JS CLICK จึงทำงาน สำหรับการคลิกอย่างใดอย่างหนึ่งนั้นล้วนเป็นการคลิกเฉลี่ย มันก็เหมือนปืนด้วย1 กระบอกและ2 ทริกเกอร์ หนึ่งจากวิวพอร์ตหนึ่งจาก JS เนื่องจากPhamtomJSยอดเยี่ยมในการจำลองกลไกของเบราว์เซอร์การคลิก JS ควรทำงานได้อย่างสมบูรณ์

ครั้งที่สอง ตัวจัดการเหตุการณ์ของ "คลิก" ต้องเชื่อมโยงในช่วงเวลาที่ไม่ดี

ตัวอย่างเช่นเราได้ไฟล์ <div>

  • -> เราทำการคำนวณบางอย่าง

  • -> จากนั้นเราจะเชื่อมโยงเหตุการณ์ของการคลิกกับไฟล์<div>.

  • -> บวกกับการเข้ารหัสเชิงมุมที่ไม่ดี (เช่นไม่จัดการวงจรของขอบเขตอย่างถูกต้อง)

เราอาจจบลงด้วยผลลัพธ์เดียวกัน การคลิกไม่ทำงานเนื่องจาก WebdriverJS พยายามคลิกที่องค์ประกอบเมื่อไม่มีตัวจัดการเหตุการณ์การคลิก

เหตุใด JS CLICK จึงทำงาน Js click ก็เหมือนกับการฉีด js ลงในเบราว์เซอร์โดยตรง เป็นไปได้ด้วย 2 วิธี

Fistผ่านคอนโซล devtools (ใช่ WebdriverJS สื่อสารกับคอนโซลของ devtools)

ประการที่สองคือใส่<script>แท็กลงใน HTML โดยตรง

สำหรับแต่ละเบราว์เซอร์ลักษณะการทำงานจะแตกต่างกัน แต่ไม่ว่าวิธีการเหล่านี้จะซับซ้อนกว่าการคลิกที่ปุ่ม คลิกกำลังใช้สิ่งที่มีอยู่แล้ว (ผู้ใช้คลิก) การคลิก js จะผ่านแบ็คดอร์

และสำหรับ js click จะดูเหมือนเป็นงานแบบอะซิงโครนัส สิ่งนี้เกี่ยวข้องกับหัวข้อที่ซับซ้อนของ ' งานอะซิงโครนัสของเบราว์เซอร์และการตั้งเวลางาน CPU ' (อ่านสักพักกลับไม่พบบทความอีกเลย) สำหรับระยะสั้นสิ่งนี้ส่วนใหญ่จะเป็นผลเนื่องจากการคลิก js จะต้องรอรอบการจัดตารางงานของ CPUและจะทำงานช้าลงเล็กน้อยหลังจากการเชื่อมโยงของเหตุการณ์การคลิก (คุณสามารถทราบกรณีนี้เมื่อคุณพบว่าบางครั้งองค์ประกอบคลิกได้บางครั้งก็ไม่ได้)

สิ่งนี้เกิดขึ้นเมื่อใดและข้อเสียของวิธีแก้ปัญหานี้คืออะไร (ถ้ามี)

=> ตามที่กล่าวไว้ข้างต้นทั้งสองหมายถึงจุดประสงค์เดียว แต่เกี่ยวกับการใช้ทางเข้าใด:

  • คลิก: กำลังใช้สิ่งที่ให้โดยค่าเริ่มต้นของเบราว์เซอร์
  • JS click: กำลังผ่านแบ็คดอร์

=> สำหรับประสิทธิภาพนั้นยากที่จะพูดเพราะมันขึ้นอยู่กับเบราว์เซอร์ แต่โดยทั่วไป:

  • คลิก: ไม่ได้หมายความว่าเร็วกว่า แต่ลงนามในตำแหน่งที่สูงกว่าในรายการกำหนดการของงานการเรียกใช้ CPU เท่านั้น
  • JS click: ไม่ได้หมายความว่าช้าลง แต่ลงชื่อเข้าใช้ในตำแหน่งสุดท้ายของรายการกำหนดการของงาน CPU เท่านั้น

=> ข้อเสีย:

  • คลิก: ดูเหมือนจะไม่มีข้อเสียยกเว้นคุณใช้ PhamtomJS
  • JS click: ไม่ดีต่อสุขภาพ คุณอาจคลิกบางสิ่งที่ไม่มีในมุมมองโดยไม่ได้ตั้งใจ เมื่อคุณใช้สิ่งนี้ตรวจสอบให้แน่ใจว่ามีองค์ประกอบอยู่และพร้อมให้ดูและคลิกเป็นมุมมองของผู้ใช้ปลายทาง

ปล. หากคุณกำลังมองหาวิธีแก้ปัญหา

  • ใช้ PhantomJS? ฉันจะแนะนำให้ใช้ Chrome headless แทน ใช่คุณสามารถตั้งค่า Chrome headless บน Ubuntu ได้ สิ่งนี้ทำงานเหมือนกับ Chrome แต่ไม่มีเพียงมุมมองและบั๊กน้อยเช่น PhantomJS
  • ไม่ได้ใช้ PhamtomJS แต่ยังคงมีปัญหา? ฉันจะแนะนำให้ใช้ที่คาดหวังเงื่อนไขของไม้โปรแทรกเตอร์ด้วยbrowser.wait()( ตรวจสอบสิ่งนี้สำหรับข้อมูลเพิ่มเติม )

(อยากจะทำให้สั้น แต่จบไม่ดีอะไรที่เกี่ยวกับทฤษฎีซับซ้อนจะอธิบาย ... )

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.