WebDriver click () против JavaScript click ()

127

История:

Здесь, в StackOverflow, я видел, как пользователи сообщали, что они не могут щелкнуть элемент с помощью команды selenium WebDriver «click» и могут обойти это с помощью щелчка 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 - нет? Когда именно это происходит и каковы недостатки этого обходного пути (если таковые имеются)?

Я лично использовал этот обходной путь, не понимая полностью, почему я должен это делать и к каким проблемам это может привести.

alecxe
источник

Ответы:

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 работает, а щелчок WebDriver - нет?

Как я уже упоминал выше, WebDriver попытается максимально точно смоделировать то, что происходит, когда реальный пользователь использует браузер. Дело в том, что DOM может содержать элементы, с которыми пользователь не может взаимодействовать, и WebDriver не позволит вам щелкнуть по этим элементам. Помимо упомянутого мною случая перекрытия, это также влечет за собой невозможность щелчка по невидимым элементам. Типичный случай, который я вижу в вопросах о переполнении стека, - это кто-то, кто пытается взаимодействовать с элементом графического интерфейса, который уже существует в DOM, но становится видимым только тогда, когда был изменен какой-либо другой элемент. Иногда это происходит с раскрывающимися меню: вам нужно сначала нажать кнопку, чтобы открыть раскрывающийся список, прежде чем можно будет выбрать пункт меню. Если кто-то попытается щелкнуть элемент меню до того, как меню станет видимым,Если человек затем попытается сделать это с помощью JavaScript, это сработает, потому что событие доставляется непосредственно элементу, независимо от видимости.

Когда следует использовать JavaScript для кликов?

Если вы используете Selenium для тестирования приложения , я отвечу на этот вопрос «почти никогда». По большому счету, ваш тест Selenium должен воспроизводить то, что пользователь сделал бы с браузером. Возьмем пример раскрывающегося меню: тест должен сначала нажать на кнопку, которая вызывает раскрывающееся меню, а затем щелкнуть элемент меню. Если есть проблема с графическим интерфейсом пользователя, потому что кнопка невидима, или кнопка не отображает элементы меню, или что-то подобное, тогда ваш тест завершится неудачно, и вы обнаружите ошибку. Если вы используете JavaScript для щелчка мышью, вы не сможете обнаружить эти ошибки с помощью автоматического тестирования.

Я говорю «почти никогда», потому что могут быть исключения, когда имеет смысл использовать JavaScript. Однако они должны быть очень редкими.

Если вы используете Selenium для сканирования сайтов , то не так важно пытаться воспроизвести поведение пользователя. Так что использование JavaScript для обхода графического интерфейса менее проблематично.

Луис
источник
1
Гораздо лучший ответ, это должен быть принятый. В приведенном выше ответе говорится о граничном случае, характерном для PhantomJS, это гораздо более точный ИМХО.
Ardesco
@Ardesco определенно. Помечено как принятое. Прекрасный и довольно подробный ответ.
alecxe
Вручение награды Лин, как и планировалось, но я начну новую, чтобы поблагодарить вас за еще один отличный ответ. Спасибо.
alecxe
1
Очень хороший и понятный ответ. По моему опыту, у WebDriver было много проблем со страницами AngularJS: с некоторыми элементами методы WebDriver вроде clickили sendKeysработали, но не всегда. Не было проблем с обнаружением или других исключений, тестовый пример просто не продвигался дальше. Логирование показало, что действие было выполнено. Иногда Actionпомогало использование . Если я щелкнул элемент вручную (после остановки тестового примера), все работало нормально. Поэтому в тех случаях мы перешли на необработанный JS. Я не работал с AngularJS последние 2 года, так что сейчас все может быть лучше.
Würgspaß
1
@Alex У меня нет удобной ссылки. Мой ответ основан на опыте работы с Selenium в частности и с моделированием пользовательских событий в целом. Я начал использовать Selenium 5 лет назад. За время, пока я использовал Selenium, мне приходилось читать код Selenium, чтобы исправить некоторые проблемы, и я отправил довольно много отчетов об ошибках, связанных с отправкой событий, и обсудил ошибки с разработчиками Selenium. «как можно лучше» - вот цель. Я определенно встречал (теперь исправленные) ошибки, которые не позволяли достичь этой цели. Некоторые ошибки могут остаться.
Луи
30

Щелчок, выполняемый драйвером, пытается имитировать поведение реального пользователя как можно точнее, в то время как JavaScript HTMLElement.click()выполняет действие по умолчанию для clickсобытия, даже если элемент не взаимодействует.

Отличия заключаются в следующем:

  • Драйвер обеспечивает видимость элемента , прокручивая его в представлении, и проверяет, что с элементом можно взаимодействовать .

    Драйвер выдаст ошибку:

    • когда элемент сверху в координатах щелчка не является целевым элементом или потомком
    • когда элемент не имеет положительного размера или полностью прозрачен
    • когда элемент является инвалидом ввода или кнопка (атрибут / свойством 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);


  • Целевыми элемент Концы-вверх , охватываемые плавающий элемент один раз прокручивается в поле зрения:

    Драйвер автоматически прокручивает элемент в представлении, чтобы сделать его видимым. Если страница содержит плавающий / липкий элемент (меню, реклама, нижний колонтитул, уведомление, политика файлов 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

ПРИМЕЧАНИЕ: назовем «щелчок» щелчком конечного пользователя. js click - это щелчок через JS

Почему щелчок «через JavaScript» работает, а обычный щелчок WebDriver - нет?

Это может произойти в двух случаях:

I. Если вы используете PhamtomJS

Тогда это наиболее распространенное известное поведение PhantomJS. Например, некоторые элементы иногда не доступны для нажатия <div>. Это потому, что PhantomJSизначально было сделано для моделирования движка браузеров (например, исходный HTML + CSS -> вычисление CSS -> рендеринг). Но это не означает, что с ним следует взаимодействовать как с конечным пользователем (просмотр, щелчок, перетаскивание). Поэтому PhamtomJSтолько частично поддерживается взаимодействием с конечными пользователями.

ПОЧЕМУ РАБОТАЕТ JS CLICK? Что касается любого щелчка, все они являются средним щелчком. Это как ружье с 1 стволом и 2 спусковыми крючками . Один из области просмотра, один из JS. Поскольку он PhamtomJSотлично подходит для моделирования движка браузера, щелчок JS должен работать идеально.

II. Обработчик события «щелчок» попал на привязку в плохой период времени.

Например, мы получили <div>

  • -> Делаем некоторые расчеты

  • -> затем мы привязываем событие щелчка к объекту <div>.

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

Мы можем получить тот же результат. Щелчок не будет работать, потому что WebdriverJS пытается щелкнуть элемент, когда у него нет обработчика события щелчка.

ПОЧЕМУ РАБОТАЕТ JS CLICK? Js click - это как инъекция js прямо в браузер. Возможно 2 способами,

Кулак через консоль devtools (да, WebdriverJS действительно взаимодействует с консолью devtools).

Во-вторых , вставьте <script>тег прямо в HTML.

Для каждого браузера поведение будет разным. Но, тем не менее, эти методы более сложны, чем нажатие на кнопку. Click использует то, что уже есть (клик конечных пользователей), js click проходит через бэкдор.

А для js щелчок будет асинхронной задачей. Это связано со сложной темой « асинхронные задачи браузера и планирование задач ЦП » (прочтите это некоторое время назад, не можете найти статью снова). Короче говоря, это в основном приведет к тому, что js click будет ждать цикла планирования задач ЦП, и он будет работать немного медленнее после привязки события click. (Вы могли знать этот случай, когда обнаружили, что элемент иногда кликабелен, а иногда нет.)

Когда именно это происходит и каковы недостатки этого обходного пути (если таковые имеются)?

=> Как упоминалось выше, оба предназначены для одной цели, но об использовании входа:

  • Щелчок: используется то, что предоставляется браузером по умолчанию.
  • JS click: проходит через бэкдор.

=> Что касается производительности, трудно сказать, потому что она зависит от браузеров. Но в целом:

  • Щелчок: не означает более быстрое, а только подписанное более высокое положение в списке расписания задачи выполнения ЦП.
  • JS-щелчок: не означает медленнее, но только он вошел в последнюю позицию списка расписания задачи ЦП.

=> Минусы:

  • Click: похоже, не имеет никаких недостатков, за исключением того, что вы используете PhamtomJS.
  • JS click: очень вредно для здоровья. Вы можете случайно нажать на то, чего нет в обзоре. Когда вы используете это, убедитесь, что элемент присутствует и доступен для просмотра и щелчка с точки зрения конечного пользователя.

PS если ищете решение.

  • Используете PhantomJS? Вместо этого я предлагаю использовать Chrome без головы. Да, вы можете настроить Chrome без головы на Ubuntu. Вещь работает так же, как Chrome, но только не имеет представления и менее глючна, как PhantomJS.
  • Вы не используете PhamtomJS, но проблемы по-прежнему возникают? Я предлагаю использовать ExpectedCondition of Protractor с browser.wait()( проверьте это для получения дополнительной информации )

(Я хочу быть кратким, но в итоге все закончилось плохо. Все, что связано с теорией, сложно объяснить ...)

Линь Фам
источник