Kliknięcie WebDriver () a kliknięcie JavaScript ()

126

Historia:

Tutaj, na StackOverflow, widziałem użytkowników zgłaszających, że nie mogą kliknąć elementu za pomocą polecenia selenium WebDriver „kliknięcie” i mogą obejść go za pomocą kliknięcia JavaScript, wykonując skrypt.

Przykład w Pythonie:

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

Przykład w WebDriverJS / Protractor:

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

Pytanie:

Dlaczego kliknięcie „przez JavaScript” działa, podczas gdy zwykłe kliknięcie WebDriver nie? Kiedy dokładnie to się dzieje i jakie są wady tego obejścia (jeśli w ogóle)?

Osobiście zastosowałem to obejście bez pełnego zrozumienia, dlaczego muszę to zrobić i do jakich problemów może to prowadzić.

alecxe
źródło

Odpowiedzi:

150

Wbrew temu, co sugeruje obecnie akceptowana odpowiedź , PhantomJS nie ma nic szczególnego, jeśli chodzi o różnicę między wykonaniem kliknięcia przez WebDriver a zrobieniem tego w JavaScript.

Różnica

Zasadnicza różnica między tymi dwiema metodami jest wspólna dla wszystkich przeglądarek i można ją wyjaśnić w prosty sposób:

  • WebDriver: Kiedy WebDriver wykonuje kliknięcie, stara się możliwie najlepiej zasymulować, co się dzieje, gdy prawdziwy użytkownik korzysta z przeglądarki. Załóżmy, że masz element A, który jest przyciskiem z napisem „Kliknij mnie” i element B, który jest divelementem przezroczystym, ale ma swoje wymiary i jest zIndexustawiony tak, że całkowicie zakrywa A. Następnie mówisz WebDriverowi, aby kliknął A. WebDriver to zasymuluj kliknięcie, tak aby B jako pierwszy otrzymał kliknięcie . Czemu? Ponieważ B obejmuje A, a jeśli użytkownik próbowałby kliknąć A, B najpierw otrzyma zdarzenie. To, czy A ostatecznie otrzyma zdarzenie kliknięcia, zależy od tego, jak B obsłuży to zdarzenie. W każdym razie zachowanie w przypadku WebDriver w tym przypadku jest takie samo, jak w przypadku, gdy prawdziwy użytkownik próbuje kliknąć A.

  • JavaScript: Teraz załóżmy, że używasz JavaScript do zrobienia A.click(). Ta metoda klikania nie odtwarza tego, co naprawdę dzieje się, gdy użytkownik próbuje kliknąć A. JavaScript wysyła clickzdarzenie bezpośrednio do A, a B nie otrzyma żadnego zdarzenia.

Dlaczego kliknięcie JavaScript działa, gdy kliknięcie WebDriver nie?

Jak wspomniałem powyżej, WebDriver postara się jak najlepiej zasymulować to, co dzieje się, gdy prawdziwy użytkownik korzysta z przeglądarki. Faktem jest, że DOM może zawierać elementy, z którymi użytkownik nie może wchodzić w interakcje, a WebDriver nie pozwoli na kliknięcie tych elementów. Oprócz wspomnianego przeze mnie przypadku nakładania się oznacza to również, że niewidoczne elementy nie mogą być klikane. Typowy przypadek, który widzę w pytaniach Stack Overflow, to ktoś, kto próbuje wejść w interakcję z elementem GUI, który już istnieje w DOM, ale staje się widoczny tylko wtedy, gdy jakiś inny element został zmanipulowany. Zdarza się to czasami w przypadku menu rozwijanych: musisz najpierw kliknąć przycisk, aby wyświetlić menu rozwijane, zanim będzie można wybrać element menu. Jeśli ktoś spróbuje kliknąć element menu, zanim menu zostanie wyświetlone,Jeśli dana osoba spróbuje to zrobić za pomocą JavaScript, zadziała, ponieważ zdarzenie jest dostarczane bezpośrednio do elementu, niezależnie od widoczności.

Kiedy należy używać JavaScript do klikania?

Jeśli używasz Selenium do testowania aplikacji , moja odpowiedź na to pytanie brzmi „prawie nigdy”. Ogólnie rzecz biorąc, test Selenium powinien odtwarzać to, co użytkownik zrobiłby z przeglądarką. Na przykładzie menu rozwijanego: test powinien najpierw kliknąć przycisk, który powoduje wyświetlenie listy rozwijanej, a następnie kliknąć pozycję menu. Jeśli wystąpi problem z graficznym interfejsem użytkownika, ponieważ przycisk jest niewidoczny lub przycisk nie wyświetla elementów menu lub coś podobnego, test zakończy się niepowodzeniem i wykryjesz błąd. Jeśli używasz JavaScript do klikania, nie będziesz w stanie wykryć tych błędów za pomocą testów automatycznych.

Mówię „prawie nigdy”, ponieważ mogą istnieć wyjątki, w których użycie JavaScript ma sens. Powinny być jednak bardzo rzadkie.

Jeśli używasz Selenium do skrobania witryn , próba odtworzenia zachowania użytkownika nie jest tak krytyczna. Więc użycie JavaScript do ominięcia GUI jest mniejszym problemem.

Louis
źródło
1
O wiele lepsza odpowiedź, powinna być akceptowana. Powyższa odpowiedź mówi o skrajnym przypadku specyficznym dla PhantomJS, ten jest znacznie dokładniejszy IMHO.
Ardesco
@Ardesco zdecydowanie. Oznaczono jako zaakceptowane. Doskonała i dość szczegółowa odpowiedź.
alecxe
Przyznanie nagrody Linh zgodnie z planem, ale zacznę nową, aby podziękować za kolejną niesamowitą odpowiedź. Dzięki.
alecxe
1
Bardzo dobra i zrozumiała odpowiedź. Z mojego doświadczenia wynika, że ​​WebDriver miał wiele problemów ze stronami AngularJS: Z niektórymi elementami metody WebDriver były takie same clicklub sendKeysdziałały - ale nie zawsze. Nie było problemu z lokalizacją ani innego wyjątku, przypadek testowy po prostu nie posunął się dalej. Rejestrowanie wykazało, że akcja została wykonana. Czasami użycie Actionpomogło. Gdybym kliknął element ręcznie (po zatrzymaniu przypadku testowego), wszystko działało dobrze. W takich przypadkach przerzucaliśmy się na surowy JS. Nie pracowałem z AngularJS przez ostatnie 2 lata, więc teraz może być lepiej.
Würgspaß
1
@Alex Nie mam przydatnego linku. Moja odpowiedź wywodzi się w szczególności z doświadczeń z Selenium i ogólnie z symulowania zdarzeń użytkownika. Zacząłem używać Selenium 5 lat temu. W czasie, gdy korzystałem z Selenium, musiałem czytać kod Selenium, aby naprawić niektóre problemy, i złożyłem kilka raportów o błędach dotyczących wysyłania zdarzeń i omówiłem błędy z programistami Selenium. Celem jest „jak najlepiej”. Z pewnością napotkałem (teraz naprawione) błędy, które uniemożliwiły mu osiągnięcie tego celu. Niektóre błędy mogą pozostać.
Louis
30

Kliknięcie wykonane przez sterownik próbuje zasymulować zachowanie prawdziwego użytkownika tak blisko, jak to możliwe, podczas gdy JavaScript HTMLElement.click()wykonuje domyślną akcję dla clickzdarzenia, nawet jeśli element nie jest interaktywny.

Różnice są następujące:

  • Sterownik zapewnia, że ​​element jest widoczny , przewijając go do widoku i sprawdza, czy element jest interaktywny .

    Sterownik zgłosi błąd:

    • gdy element na górze na współrzędnych kliknięcia nie jest elementem docelowym ani elementem podrzędnym
    • gdy element nie ma rozmiaru dodatniego lub jest w pełni przezroczysty
    • gdy element jest wyłączonym wejściem lub przyciskiem (atrybut / właściwość disabledto true)
    • gdy element ma wyłączony wskaźnik myszy (CSS pointer-eventsjest none)


    JavaScript HTMLElement.click()zawsze wykona domyślną akcję lub w najlepszym przypadku po cichu zawiedzie, jeśli element jest wyłączony.

  • Oczekuje się, że kierowca wyostrzy element, jeśli można go ustawić .

    JavaScript HTMLElement.click()nie będzie.

  • Oczekuje się, że sterownik będzie emitował wszystkie zdarzenia (przesunięcie myszy, przesunięcie myszy, przesunięcie myszy, kliknięcie, ...) tak jak prawdziwy użytkownik.

    JavaScript HTMLElement.click()emituje tylko clickzdarzenie. Strona może polegać na tych dodatkowych zdarzeniach i zachowywać się inaczej, jeśli nie są emitowane.

    Oto zdarzenia emitowane przez sterownik po kliknięciu w przeglądarce 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, ... }
    

    A oto zdarzenie emitowane przez wstrzyknięcie JavaScript:

    click {target:#topic, clientX:0, clientY:0, isTrusted:false, ... }
  • Zdarzenie emitowane przez JavaScript .click() nie jest zaufane i nie można wywołać domyślnej akcji:

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

    Zwróć uwagę, że niektóre sterowniki nadal generują niezaufane zdarzenia. Tak jest w przypadku PhantomJS od wersji 2.1.

  • Zdarzenie emitowane przez JavaScript .click() nie ma współrzędnych kliknięcia .

    Właściwości clientX, clientY, screenX, screenY, layerX, layerYsą ustawione na 0. Strona może na nich polegać i zachowywać się inaczej.


Może być w porządku użycie JavaScript .click()do zgarniania niektórych danych, ale nie jest to w kontekście testowym. Niweczy cel testu, ponieważ nie symuluje zachowania użytkownika. Tak więc, jeśli kliknięcie ze sterownika nie powiedzie się, prawdziwy użytkownik najprawdopodobniej również nie wykona tego samego kliknięcia w tych samych warunkach.


Co sprawia, że ​​kierowca nie klika elementu, gdy spodziewamy się jego sukcesu?

  • Docelowy element nie jest jeszcze widoczny / interaktywny z powodu opóźnienia lub efektu przejścia.

    Kilka przykładów :

    https://developer.mozilla.org/fr/docs/Web (rozwijane menu nawigacji) http://materializecss.com/side-nav.html (rozwijany pasek boczny)

    Obejścia:

    Dodaj kelnera, aby czekał na widoczność, minimalny rozmiar lub stałą pozycję:

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

    Ponów próbę kliknięcia, aż się powiedzie:

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

    Dodaj opóźnienie pasujące do czasu trwania animacji / przejścia:

    browser.sleep(250);


  • Docelowy element kończy się przykryty elementem pływającym po przewinięciu do widoku:

    Sterownik automatycznie przewija element do widoku, aby był widoczny. Jeśli strona zawiera element pływający / przyklejony (menu, reklamy, stopka, powiadomienie, polityka dotycząca plików cookie…), element może zostać zakryty i nie będzie już widoczny / interaktywny.

    Przykład: https://twitter.com/?lang=en

    Obejścia:

    Ustaw większy rozmiar okna, aby uniknąć przewijania lub pływającego elementu.

    Najedź na element z ujemnym Yprzesunięciem, a następnie kliknij go:

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

    Przed kliknięciem przewiń element do środka okna:

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

    Ukryj element pływający, jeśli nie można tego uniknąć:

    browser.executeScript(function scrollCenter(elem) {
      elem.style.display = 'none';
    }, element);
    
Florent B.
źródło
17

UWAGA: nazwijmy „kliknięciem” kliknięciem użytkownika końcowego. „js click” to kliknięcie przez JS

Dlaczego kliknięcie „przez JavaScript” działa, podczas gdy zwykłe kliknięcie WebDriver nie?

Istnieją 2 przypadki, w których może się to zdarzyć:

I. Jeśli używasz PhamtomJS

W takim razie jest to najczęstsze znane zachowanie PhantomJS. Na przykład niektóre elementy czasami nie są klikalne <div>. Dzieje się tak, ponieważ PhantomJSzostał stworzony do symulacji silnika przeglądarek (jak początkowy HTML + CSS -> obliczeniowy CSS -> renderowanie). Ale nie oznacza to interakcji jako sposobu użytkownika końcowego (przeglądanie, klikanie, przeciąganie). Dlatego PhamtomJSjest tylko częściowo wspierany przez interakcję z użytkownikiem końcowym.

DLACZEGO JS CLICK DZIAŁA? Jeśli chodzi o jedno kliknięcie, wszystkie oznaczają kliknięcie średnie. Jest jak pistolet z 1 lufą i 2 spustami . Jeden z widoku, jeden z JS. Ponieważ PhamtomJSświetnie symuluje silnik przeglądarki, kliknięcie JS powinno działać idealnie.

II. Program obsługi zdarzenia „kliknięcie” musiał się wiązać w złym okresie.

Na przykład otrzymaliśmy plik <div>

  • -> Wykonujemy obliczenia

  • -> wtedy łączymy zdarzenie kliknięcia z plikiem <div>.

  • -> Plus z pewnym złym kodowaniem kąta (np. Niewłaściwa obsługa cyklu lunety)

Możemy otrzymać ten sam rezultat. Kliknięcie nie zadziała, ponieważ WebdriverJS próbuje kliknąć element, gdy nie ma on obsługi zdarzenia kliknięcia.

DLACZEGO JS CLICK DZIAŁA? Js click jest jak wstrzyknięcie js bezpośrednio do przeglądarki. Możliwe na 2 sposoby,

Pięść jest za pośrednictwem konsoli DevTools (tak, WebdriverJS ma komunikować się z konsolą DevTools').

Drugi jest wstrzyknąć <script>tagu bezpośrednio w HTML.

Zachowanie będzie inne dla każdej przeglądarki. Ale niezależnie od tego, te metody są bardziej skomplikowane niż kliknięcie przycisku. Kliknięcie wykorzystuje to, co już tam jest (klikają użytkownicy końcowi), kliknięcie js przechodzi przez tylne drzwi.

W przypadku js kliknięcie będzie wyglądać jak zadanie asynchroniczne. Jest to związane z dość złożonym tematem „ asynchroniczne zadanie przeglądarki i planowanie zadań procesora ” (przeczytaj go chwilę wstecz, nie mogę znaleźć artykułu ponownie). Krótko mówiąc, będzie to głównie skutkować tym, że js click będzie musiało czekać na cykl planowania zadań procesora i będzie działać nieco wolniej po powiązaniu zdarzenia click. (Możesz znać ten przypadek, gdy znalazłeś element, który czasami można kliknąć, a czasem nie).

Kiedy dokładnie to się dzieje i jakie są wady tego obejścia (jeśli w ogóle)?

=> Jak wspomniano powyżej, oba mają na celu jeden cel, ale o używaniu którego wejścia:

  • Click: używa tego, co zapewnia domyślnie przeglądarka.
  • JS klik: przechodzi przez backdoor.

=> Jeśli chodzi o wydajność, trudno powiedzieć, ponieważ opiera się na przeglądarkach. Ale ogólnie:

  • Kliknięcie: nie oznacza szybciej, ale tylko podpisana wyższa pozycja na liście harmonogramów zadań wykonania procesora.
  • Kliknięcie JS: nie oznacza wolniejszego, ale tylko wpisało się na ostatnią pozycję listy harmonogramu zadań procesora.

=> Wady:

  • Kliknięcie: nie wydaje się mieć żadnych wad, z wyjątkiem tego, że używasz PhamtomJS.
  • Kliknięcie JS: bardzo szkodliwe dla zdrowia. Możesz przypadkowo kliknąć coś, czego nie ma w widoku. Kiedy używasz tego, upewnij się, że element jest tam i dostępny do wyświetlenia i kliknięcia jako punkt widzenia użytkownika końcowego.

PS jeśli szukasz rozwiązania.

  • Korzystasz z PhantomJS? Zamiast tego zasugeruję użycie Chrome Headless. Tak, możesz skonfigurować Chrome bezgłowy na Ubuntu. Rzecz działa podobnie jak Chrome, ale nie ma tylko widoku i jest mniej błędna jak PhantomJS.
  • Nie używasz PhamtomJS, ale nadal masz problemy? Zasugeruję użycie ExpectedCondition of Protractor z browser.wait()( sprawdź to, aby uzyskać więcej informacji )

(Chcę, żeby to było krótkie, ale skończyło się źle. Wszystko, co dotyczy teorii, jest trudne do wyjaśnienia ...)

Linh Pham
źródło