WebDriver click () та JavaScript click ()


127

Історія:

Тут, на StackOverflow, я бачив користувачів, які повідомляють, що вони не можуть натиснути елемент через команду "click" селену 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 в цьому випадку така ж, як і тоді, коли реальний користувач намагається натиснути на А.

  • JavaScript: Тепер, припустимо, для цього ви використовуєте JavaScript A.click(). Цей спосіб клацання не відтворює те, що відбувається насправді, коли користувач намагається натиснути А. JavaScript посилає clickподію безпосередньо на A, а B не отримає жодної події.

Чому клік JavaScript працює, коли натискання WebDriver не працює?

Як я вже згадував вище, WebDriver намагатиметься максимально імітувати те, що відбувається, коли реальний користувач використовує браузер. Справа в тому, що DOM може містити елементи, з якими користувач не може взаємодіяти, а WebDriver не дозволить вам натискати на цей елемент. Окрім згаданого я випадку, що перекривається, це також спричиняє, що невидимі елементи не можна натискати. Поширений випадок, який я бачу в питаннях переповнення стека - це той, хто намагається взаємодіяти з елементом GUI, який вже існує в DOM, але стає видимим лише тоді, коли іншим елементом було маніпульовано. Іноді це трапляється із меню, що випадає: вам потрібно спочатку натиснути кнопку, що відображає спадне меню, перш ніж можна вибрати пункт меню. Якщо хтось намагається натиснути на пункт меню, перш ніж меню буде видно,Якщо людина потім спробує зробити це за допомогою JavaScript, він буде працювати, оскільки подія доставляється безпосередньо до елемента, незалежно від видимості.

Коли слід використовувати JavaScript для клацання?

Якщо ви використовуєте Selenium для тестування програми , моя відповідь на це питання "майже ніколи". За великим рахунком, ваш тест Selenium повинен відтворювати, що зробив би користувач із браузером. Візьмемо приклад випадаючого меню: тест повинен натиснути кнопку, яка спочатку відкриває спадне меню, а потім натиснути на пункт меню. Якщо з графічним інтерфейсом є проблеми, оскільки кнопка невидима, або кнопка не відображає пункти меню чи щось подібне, ваш тест не вдасться, і ви виявили помилку. Якщо ви користуєтесь JavaScript, щоб натиснути, ви не зможете виявити ці помилки за допомогою автоматичного тестування.

Я кажу "майже ніколи", оскільки можуть бути винятки, коли є сенс використовувати JavaScript. Однак вони повинні бути дуже рідкісними.

Якщо ви використовуєте Selenium для скреблінгу сайтів , намагатися відтворити поведінку користувачів не так важливо. Тому використання JavaScript для обходу графічного інтерфейсу - це менше проблеми.


1
Набагато краще відповісти, це має бути прийнятим. Відповідь вище стосується кращого випадку, характерного для PhantomJS, цей набагато більш точний IMHO.
Ардеско

@Ardesco точно. Позначено як прийняте. Ідеальна і досить детальна відповідь.
alecxe

Вручення винагороди Ліну, як планувалося, але я розпочну новий, щоб подякувати за ще одну приголомшливу відповідь. Дякую.
alecxe

1
Дуже хороша і зрозуміла відповідь. На мій досвід, у WebDriver було багато проблем зі сторінками AngularJS: З деякими елементами методи WebDriver подобаються clickабо sendKeysпрацювали - але не завжди. Не було жодної проблеми локалізації чи іншого винятку, тестовий випадок просто не просувався далі. Ведення журналу показало, що дія виконана. Іноді використання Actionдопомогло. Якщо я натиснув елемент вручну (після того, як тестовий випадок припинився), все працювало нормально. Тож ми перейшли на сирий JS з тих випадків. Останні 2 роки я не працював з AngularJS, тому зараз може бути краще.
Вюргспас

1
@Alex У мене немає зручного посилання. Моя відповідь випливає з досвіду, зокрема, із Selenium та із імітацією подій користувачів загалом. Я почав використовувати селен 5 років тому. Протягом часу, коли я використовував Selenium, мені довелося прочитати код Selenium, щоб виправити деякі проблеми, і я подав досить багато звітів про помилки щодо диспетчеризації подій та обговорював помилки з розробниками Selenium. "якнайкраще" - це мета. Я, безумовно, стикався з (зараз виправленими) помилками, які заважали йому досягти цієї мети. Деякі помилки можуть залишитися.
Луї

30

Клацання, виконане драйвером, намагається максимально наблизити поведінку реального користувача, тоді як JavaScript HTMLElement.click()виконує дію за замовчуванням для clickподії, навіть якщо елемент не є взаємодіючим.

Відмінності:

  • Драйвер гарантує, що елемент видно , прокручуючи його у подання та перевіряючи, що елемент є взаємодіючим .

    Драйвер виведе помилку:

    • коли елемент зверху в координатах клацання не є цільовим елементом або нащадком
    • коли елемент не має позитивного розміру або якщо він повністю прозорий
    • коли елемент заборонений введенням або кнопкою (атрибут / властивість disabledє true)
    • коли в елементі покажчик миші вимкнено (CSS pointer-eventsє none)


    JavaScript HTMLElement.click()завжди буде виконувати дію за замовчуванням або в кращому випадку мовчки вийде з ладу, якщо елемент вимкнено.

  • Очікується, що водій приведе елемент у фокус, якщо він фокусується .

    JavaScript HTMLElement.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 (бічна панель, що випадає)

    Робочі кола:

    Додайте офіціанта, щоб чекати на видимість, мінімальний розмір або стійке положення:

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


  • Орієнтований елемент закінчується плаваючим елементом, щойно прокручується до виду:

    Драйвер автоматично прокручує елемент у подання, щоб зробити його видимим. Якщо сторінка містить плаваючий / липкий елемент (меню, оголошення, колонтитул, повідомлення, політика щодо файлів cookie ..), елемент може закінчитися покриттям і більше не буде видимим / взаємодіючим.

    Приклад: https://twitter.com/?lang=en

    Обхідні шляхи:

    Встановіть розмір вікна на більший, щоб уникнути прокрутки або плаваючого елемента.

    Наведіть курсор на елемент з від'ємним 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

ПРИМІТКА. Давайте зателефонуємо "click" - це клік кінцевого користувача. 'js click' - це клацання через JS

Чому натискання "через JavaScript" працює, коли звичайний клік WebDriver не робить?

Є два випадки, щоб це сталося:

I. Якщо ви використовуєте PhamtomJS

Тоді це найпоширеніша відома поведінка PhantomJS. Наприклад, деякі елементи не можна натискати, наприклад <div>. Це тому, що PhantomJSбув оригінальний для моделювання двигуна браузерів (як початковий HTML + CSS -> обчислення CSS -> рендерінг). Але це не означає взаємодіяти з ним як шляхом кінцевого користувача (перегляд, клацання, перетягування). Тому PhamtomJSлише частково підтримується взаємодія кінцевих споживачів.

ЧОМУ JS CLICK РОБОТА? Що стосується будь-якого клацання, то всі вони означають клік. Це як пістолет з 1 стволом і 2 курок . Один із вікна перегляду, один - від JS. Оскільки PhamtomJSчудово моделює двигун браузера, клацання JS має працювати ідеально.

II. Обробник події "click" повинен прив'язатись у поганий проміжок часу.

Наприклад, ми отримали <div>

  • -> Ми робимо деякий розрахунок

  • -> тоді ми прив'язуємо подію клацання до <div>.

  • -> Плюс з поганим кодуванням кутового (наприклад, неправильне керування циклом області)

Ми можемо досягти того ж результату. Клацання не вийде, тому що WebdriverJS намагається натиснути на елемент, коли у нього немає обробника подій клацання.

ЧОМУ JS CLICK РОБОТА? Js click - це як введення js безпосередньо в браузер. Можливо двома способами,

Кулак здійснюється через консоль devtools (так, WebdriverJS спілкується з консоллю devtools).

Друге - ввести <script>тег безпосередньо в HTML.

Для кожного браузера поведінка буде різною. Але незалежно від цих методів складніше, ніж натискання на кнопку. Клацання використовує те, що вже є (натискають кінцеві користувачі), js клацання відбувається через бекдор.

А для js клацання виявиться асинхронним завданням. Це пов’язано з якоюсь складною темою " асинхронна задача браузера та планування задач процесора " (прочитайте її назад, не можу знову знайти статтю). Якщо коротко, це в основному призведе до того, що натискання js потрібно буде чекати циклу планування завдань CPU, і він буде запускатися трохи повільніше після прив'язки події клацання. (Ви могли б знати цей випадок, коли знайшли елемент, який інколи можна натискати, іноді ні.)

Коли саме це відбувається і в чому полягає зворотний бік цього способу (якщо він є)?

=> Як згадувалося вище, обидва означають для однієї мети, але про те, як використовувати який вхід:

  • Клацніть: використовується те, що надається браузером за замовчуванням.
  • Клацання JS: проходить через задній хід.

=> Для продуктивності важко сказати, оскільки він покладається на браузери. Але загалом:

  • Клацання: не означає швидше, а лише підписане вище місце в списку розкладу виконання завдання процесора.
  • Клацання JS: не означає повільніше, але лише воно ввійшло в останню позицію списку розкладу завдання CPU.

=> Мінуси:

  • Клацніть: не здається, що у вас є недоліки, за винятком того, що ви використовуєте PhamtomJS.
  • JS click: дуже погано для здоров'я. Ви можете випадково натиснути на щось, чого немає на екрані. Використовуючи це, переконайтеся, що елемент є і доступний для перегляду та клацання як точки зору кінцевого користувача.

PS, якщо ви шукаєте рішення.

  • Використовуючи PhantomJS? Я запропоную замість цього використати Chrome без голови. Так, ви можете налаштувати Chrome без голови на Ubuntu. Робота працює так само, як Chrome, але вона лише не має виду і менш глючна, як PhantomJS.
  • Не використовуєте PhamtomJS, але у вас все ще виникають проблеми? Я пропоную використовувати ExpectedCondition Protractor з browser.wait()( перевірте це для отримання додаткової інформації )

(Я хочу зробити це коротким, але закінчилося погано. Все, що пов'язано з теорією, пояснити складно ...)

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.