ожидание обратного вызова асинхронного Java-скрипта в Java Selenium JavaScriptExecutor - PullRequest
0 голосов
/ 18 сентября 2018

Во-первых, чтобы обратиться к очевидному

Я видел миллион и один вопрос по этому поводу с главной целью dupe: Как я могу вернуть ответ от асинхронного вызова? Этоне удовлетворяет моим потребностям, потому что

A: Я знаю, что мне уже нужен асинхронный java-скрипт и что я хочу использовать функции обратного вызова.Мне нужны подробности о том, КАК правильно их реализовать в конкретных условиях (в Selenium с использованием JavascriptExecutor и т. Д.), Которые я не смог найти из других вопросов

B: этот вопрос более специфичен для Ajax, что делает его чрезвычайносмущает меня, чтобы получить что-нибудь от этого.

C: это больше о лучшем способе ожидания завершения асинхронных вызовов, подобных этому

Что я сделал, кроме исследования переполнения стека

Я также обращался ко многимвеб-сайты, в том числе: https://www.sitepoint.com/callbacks-javascript/, которые дали мне некоторое базовое представление о том, как реализованы обратные вызовы, и я реализовал один правильно, я также нашел этот сайт https://javapapers.com/ajax/getting-started-with-ajax-using-java/, который дал мне базовое понимание одного способа, о котором я могу думать«ждать» выполнения асинхронной функции javascript, то есть пересылать данные ajax обратно в мой код, когда функция, наконец, будет выполнена.

Какую кроличью нору вы пробили?

Я хочу сделать простой скрипт, чтобы определить, вызывает ли данный веб-элемент перезагрузку страницы.Для этого мне нужно событие загрузки страницы.Я исследовал некоторое время и нашел (через несколько вопросов здесь) JavascriptExecutor и событие загрузки.Я пытался их реализовать, и я наивно думал, что это будет легким ответом.Первоначально я пытался вернуть результат добавления прослушивателя событий, когда впервые попытался реализовать это.Затем я понял, что это глупо, поэтому мне нужно было получить какое-то возвращаемое значение, я пытался пару часов возвращать различные части скрипта прослушивателя добавления событий, прежде чем понял, что использовал неправильный подход и думал о проблеме неправильно.Затем я провел исследование асинхронного JavaScript, потому что, задавая вопросы об NPE из моего предыдущего подхода, я понял, что это такое, как он используется, а затем я наткнулся на функции обратного вызова.После публикации этого и проведения дополнительных исследований я правильно реализовал простую функцию асинхронного обратного вызова.Теперь мне просто нужно правильно дождаться его завершения, прежде чем двигаться вперед, чтобы я знал, что страница полностью загружена.

всеобъемлющее описание того, что я делаю

У меня отключена темаосновной.Эта тема нажимает на веб-элемент.У веб-элемента, по которому щелкнули, есть 3 основных случая, для которых я хочу задокументировать / пометить:

  1. щелчок по нему не перезагружает страницу и не перемещается / удаляется (локаторы остаются прежними)
  2. нажатие на нее не перезагружает страницу, но локаторы элементов (например, 'id' удаляются или изменяются)
  3. нажатие веб-элемента вызывает перезагрузку страницы

InДля того чтобы задокументировать эти три случая, мне нужны флаги, событие загрузки и исключение устаревшей ссылки, и я хочу тайм-аут, если оба эти события не произошли вовремя.Если происходит загрузка, то мы знаем ее случай 3, в случае устаревшей ссылки это происходит, но загрузка не происходит.

Получить флаг для устаревшего эталонного дела было легко, его уже в Selenium.Проблема в флаге для события загрузки.Чтобы пометить это, мне нужно что-то, что я могу запустить в JavaScriptExecutor, который затем передает данные через функции асинхронного обратного вызова и, возможно, ajax (или какой-то другой метод, который я открыт для предложений) в java.Эти данные могут быть простыми числами, если я могу получить какое-то сообщение из javascript, сообщающее моему java, что оно выполнило свою асинхронную работу.Я реализовал функцию обратного вызова в моем коде, но мне нужно получить данные из JavaScript в Java.Кроме того, действительно сложно проверить синтаксис / ошибки в javascript, потому что я не могу просто проверить саму строку сценария JavaScript как свою собственную программу (до сих пор мне приходилось использовать бесплатные онлайн-редакторы, которые не так хороши).

Код, о котором идет речь

//initialize a wait object and set a default timeout (the value is ten 
//seconds)
WebDriverWait wait = new WebDriverWait(driver,timeoutSeconds);
        try{
            //wait until stalereference exception is thrown (unless timeout 
            //is exceeded)
            wait.until(ExpectedConditions.stalenessOf(webElement));

            //define the test function with a callback inside it. Prints 2 
            //to a page on page reload event only if the webelement went 
            //stale
            String script = "function test(arg,callback) {callback(arg);}" +
                    "function Return(arg){document.write(arg);}" +
                    "var a = 2;" +
                    "window.addEventListener('load', test(a,Return));";
            //TODO change this fucntionality to use ajax or some other good 
            //method to say "hey I'm done executing!" to the java code

            //execute the script to add the event listener
            JavascriptExecutor js = (JavascriptExecutor)driver;
            js.executeScript(script);

            //flag the state of this Thread
        }
        //Timeout was exceeded so the page doesn't change on click
        catch (TimeoutException e){
            //here I set a 'page didn't change' flag
        }

Примечания по коду / то, что мне нужно для кода

Единственное, что требуется для выполнения этого, должен быть веб-элемент, драйверобъект и, возможно, некоторые операторы печати, чтобы быть «флаги».Я использовал chrome-драйвер driver, потому что мне нравится chrome, и я использую поле вопроса в качестве webElement (и, конечно, удаляя мои комментарии).Мой стиль и синтаксис, вероятно, грязный, но на данный момент это всего лишь тестовый код, и я хочу, чтобы он работал.То, что я хочу, это в основном

InsideCallback.POST(somedata) to java

Но в коде, который работает (или заменяет) мой текущий тестовый код.Я знаю, что, поскольку он асинхронный, я не хочу опрашивать возвращаемый результат (что противоречит цели всей асинхронности).Я просто не знаю, как сказать «когда эта функция на другом языке возвращает свои вещи» в коде.К сожалению, у меня нет конкретной ошибки с трассировкой стека, потому что это больше о том, что НЕ в коде.Я чувствую, что на правильном пути, но мне нужно знать, является ли Ajax правильным путем.Проблема в том, что работающие примеры этого кажутся сложными и могут занять некоторое время, поэтому я хочу знать, во что я ввязываюсь.

EDIT 1

Обновлен код, поэтому в нем не должно быть синтаксических ошибок, вызывающих трассировку / сбой стека, все работало нормально, когда я пытался это сделать, только неправильное сочетание скобок и синтаксис аргументов.Мне все еще не хватает способа фактически вызвать функцию обратного вызова, но я собираюсь продолжать пытаться исследовать ее.

Редактировать 2

ОК Я наконец-то достаточно понял синтаксис javascript и получил его такчто функция обратного вызова Eventlistener работает.У меня есть печать 2 на страницу на перезагрузку.Просто и легко.Теперь мне просто нужно изменить эту функциональность, чтобы ссылаться на мой javacode и установить какой-то простой флаг.(обновил код, чтобы при запуске он работал намного лучше).Обновлен порядок кода, чтобы более точно соответствовать желаемому поведению.Необходимо добавить staleReferenceException, ДО того как будет добавлен eventListener для события onload. В противном случае события onload запускаются преждевременно для желаемого поведения.

Edit 3

Обновлен вопрос, чтобы более точно отражать заголовок / текущий статус исследования.

1 Ответ

0 голосов
/ 28 сентября 2018

Так в чем же решение?

Я обнаружил, что способ выполнения асинхронного скрипта в Java - использовать метод, предоставленный Selenium: executeAsyncScript вместо executeScript.Причина, по которой это важно, заключается в том, что я пытался использовать executeScript раньше, и сценарий работал, как предполагалось, но не так, как я хотел.Он установил слушатель JavaScript и затем вернулся.Однако я хотел вернуться к коду java, когда была сделана функция асинхронного обратного вызова, а НЕ к первоначальной функции слушателя.Эта функциональность аккуратно обеспечивается executeAsyncScript, и процесс ее обнаружения описан в моем моем вопросе:

запись в консоль Java, когда мой обратный вызов Javascript (выполненный в Selenium) возвращает

Почему ваш предыдущий подход потерпел неудачу?

Причина, по которой мой предыдущий подход не сработал, заключается в том, что я пытался вернуть данные, когда прослушиватель событий был установлен, а не когда возвращался обратный вызов.Как обсуждалось в моем вопросе, выполнялся обратный вызов DID, но я не смог связать его с Java, потому что возвращался слишком рано.Когда я использую скрипт executeAsync, он возвращается после завершения обратного вызова.Затем вы можете использовать отражение (вернуть строку в javascript, которая соответствует классу, который вы хотите вызвать, или даже метод), а затем вы можете использовать

String classToCall = (String) js.executeAsyncScript(script);
Class.forname("the string your callback returned");

, чтобы получить нужный вам класс.Затем вы можете создать статический метод и вызвать его для запуска javacode.Я сделал простой пример статического метода:

public static void JavascriptWorking(){
    System.out.println("Call back function returned");
}

И затем использовал отражение, чтобы получить этот метод из класса, который мы получили ранее, используя:

callbackMethod = clazz.getMethod("JavascriptWorking");
callbackMethod.invoke(null,null);

, который затем выводится на консольпо желанию.Одна вещь об этом - то, что код Java ждет асинхронного обратного вызова, который будет сделан.Это может привести к «бесконечным зависаниям» (обратный вызов не будет запускаться из-за некоторого прерывания), поэтому вам нужно установить свойство scriptTimeout (что легко в Selenium):

driver.manage().timeouts().setScriptTimeout(timeoutSeconds, TimeUnit.SECONDS);

Где timeoutSeconds (для меня) представляет число 10 как int.TimeUnit.SECONDS - это предопределенная константа, которая поставляется вместе с самим классом / Selenium для указания единиц измерения для тайм-аута.

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

Разве это не мешает всей цели обратных вызовов?

Весь смысл обратных вызовов заключается в том, что ваш код javascript не ждет или не опрашивает, пока не будет выполнен javascript, вместо этогопобежал, когда это должно быть.К сожалению, это решение просто заставляет Java-код ждать, пока это не произойдет, потому что выполнение программы не может продолжаться до тех пор, пока не вернется executeAsycScript.Вы можете просто создать новый поток для запуска этой конкретной части кода и заставить этот новый поток ожидать обратного вызова, чтобы остальная часть приложения не задерживалась при вызове executeAsycScript.

Что-то фундаментальное для понимания этого:

что-то, что я узнал, что имеет отношение к этому, javascript, который я создаю здесь, является на стороне клиента , потому что он запускался в МОЕМ браузере.Это означает, что я не могу / не буду использовать типичные методы Client <-> Serverside для возврата чего-либо в java из javascript, так как все здесь - Clientside.Фактически это означает, что мои разговоры о AJAX в моем вопросе не имеют отношения к реальному решению, потому что мне не нужно использовать его для возврата запроса к java-коду или чему-то в этом роде.Эти методы используются только при взаимодействии сервера и клиента.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...