Прерывистый элемент больше не привязан к ошибкам DOM с помощью watir-webdriver.

Я использую Watir-webdriver 0.5.3. Раньше с версиями около 0.4.x это никогда не было проблемой:

 while  $browser.div(:class =>  /^expander$/).exists?   
  $browser.div(:class => /^expander$/).click; sleep 1.5
 end

Щелчок вызывает выполнение некоторого javascript, который изменяет класс для щелкнутого div, расширяя элемент управления древовидной структуры. Цель скрипта — продолжать расширять узлы до тех пор, пока не останется ни одного нерасширенного.

Я часто получаю сообщение "Элемент больше не привязан к модели DOM" в строке .exists?, например. беспокоит, потому что причина, по которой я начал широко использовать .exists?, заключалась в том, чтобы избежать таких ошибок, которые в первую очередь немедленно приводят к сбою сценария.

визуально я наблюдаю расширение почти сразу после .click, и если я приостанавливаю действия прямо здесь, Firebug подтверждает, что div только с class='expander' был заменен на div с class='expander hasChildren расширен», и только этот div.

Ошибка, похоже, исходит от веб-драйвера.

У кого-нибудь еще была эта проблема или порекомендуйте обходные пути?

В более раннем вопросе мне нужно было переключить в форму /^expander$/, чтобы соответствовать исключительно этому классу, после изменения нового поведения watir-webdriver.


person Marcos    schedule 24.02.2012    source источник
comment
Я начинаю думать, что либо .exists? не видит новую версию DOM после того, как она была обновлена ​​каким-то AJAX/Javascript, либо она вообще не работает с типами элементов и поиском по тегам, которые я часто использую.   -  person Marcos    schedule 24.02.2012
comment
это очень похоже на какое-то состояние гонки, когда тест обращается к браузеру, в то время как дом все еще обновляется.. Есть ли какой-то новый объект, который «создается», который вы могли бы найти, прежде чем пытаться продолжить.. не могли бы вы возможно, подсчитайте, сколько должно быть div «expander hasChildren», и подождите, пока не появится последний, прежде чем двигаться дальше?   -  person Chuck van der Linden    schedule 24.02.2012
comment
Я пробовал такие вещи, как $browser.driver.manage().timeouts().implicit_wait=10 или смехотворно долгое явное время ожидания, но все равно та же ошибка, только более длительное ожидание. Такая проблема преследует меня с тех пор, как я начал использовать ватир. Есть ли способ принудительно перечитать DOM, чтобы мой код мог двигаться дальше? (Или прервать обновление DOM браузера, а затем перечитать, хотя я думаю, что уже пробовал send_keys ESC раньше)   -  person Marcos    schedule 24.02.2012
comment
Более релевантный ответ на ваш вопрос: визуально я наблюдаю расширение почти сразу после .click, и если я приостановлю работу прямо здесь, Firebug подтвердит, что div только с классом = expander был заменен на div вместо него с class=expander hasChildren expanded, и только этот div. Даже если на нее снова нажмут по ошибке, она никогда не вернется к expander, а expander hasChildren   -  person Marcos    schedule 24.02.2012
comment
Тем не менее, эта адская ошибка рано или поздно все равно случается неопределенно, никогда на .click, но, как ни странно, всегда на .exists? в этом примере кода.   -  person Marcos    schedule 24.02.2012
comment
Я добавил тег webdriver и немного отредактировал вопрос, чтобы внести некоторую информацию из ваших комментариев, надеясь, что, возможно, кто-то, кто лучше знает webdriver, сможет прокомментировать или предложить решение.   -  person Chuck van der Linden    schedule 28.02.2012


Ответы (3)


Что насчет этого?

while  $browser.div(:class =>  /^expander$/).exists?
  count = $browser.divs(:class => 'expander hasChildren expanded').size   
  $browser.div(:class => /^expander$/).click
  $browser.wait_until{$browser.div(:class => 'expander hasChildren expanded', :index => count).exists?}
 end

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

person Chuck van der Linden    schedule 24.02.2012
comment
Наконец-то появилась возможность протестировать это. Это дает мне can't convert FalseClass into an exact number исключение, вероятно, в .wait_until() Должны ли они быть {} вместо этого? Но я вижу, что ты там делаешь. Изучение watirwebdriver.com/waiting - person Marcos; 01.03.2012
comment
Принимая предложение вашего комментария, что вы думаете об этом: hitlist = $browser.divs(:class => /^expander$/); hitlist.reverse.each {|r| r.click; sleep .1}; $browser.wait_until { $browser.divs(:class => /^expander$/).size < 1} Кажется, он щелкнет все из них почти одновременно, но после цикла будет ждать, пока не останется 0 из них - person Marcos; 01.03.2012
comment
Я думаю, вам нужно попробовать и посмотреть, это может сильно зависеть от скорости браузера и его способности буферизовать действия во время работы js, или, возможно, некоторые клики могут быть пропущены ... но это всего лишь догадка , лучше всего запустить его по-настоящему. Я бы попробовал это с IRB сам, открыв браузер, вручную перейдя на страницу, затем вставив фрагмент кода, который вы хотите протестировать, и посмотреть, что произойдет... некоторые из этих вещей, из-за всего взаимодействия между браузер, JS, изменяющий dom, и код, управляющий браузером, вы просто не узнаете, пока не попробуете. - person Chuck van der Linden; 01.03.2012
comment
.wait_until требует выражения, которое будет оцениваться как истинное или ложное, а не блок кода, поэтому скобки должны быть правильными. если это строка, выдающая ошибку, интересно, не является ли count по какой-то причине целым числом? Я думал, что .size во второй строке выше вернет int, но, возможно, я не понимаю, какой метод там использовать, возможно, это должен быть .length (я склонен путаться между этими двумя) - person Chuck van der Linden; 02.03.2012
comment
У меня есть подозрение, что это .wait_until(seconds) { boolean test block }, но эта ссылка выше выглядит устаревшей. 30 секунд по умолчанию, если () отсутствует. Я думаю, что .size правильно. - person Marcos; 02.03.2012

Попробуй это

Watir::Wait.until do
  $browser.div(:class => /^expander$/).click
  not $browser.div(:class => /^expander$/).exists?
end
person p0deje    schedule 24.02.2012
comment
Спасибо, но это также дает сбой после расширения первого на странице только с одной записью расширения. Тем не менее, первый из них нажимается и расширяется, а также получает новый class="expander hasChildren expanded". - person Marcos; 24.02.2012
comment
Тогда вам просто нужно исправить локатор регулярных выражений. Попробуйте $browser.div(:class => /expander/) или, если вы используете последний Watir-WebDriver, он поддерживает частичные классы без регулярного выражения - $browser.div(:class => 'expander') - person p0deje; 25.02.2012
comment
Это возвращает меня к квадрату один в что мне пришлось модифицировать весь мой старый код с помощью регулярного выражения /^...$/ вместо простых кавычек, но спасибо. Я думаю, что ответ Чака, как правило, влияет на меня; семантика глубже во взаимодействии Selenium/DOM, чем (я думаю) пользователь Watir API должен беспокоиться. - person Marcos; 25.02.2012
comment
поскольку оба имени класса начинаются с расширителя, ему нужно использовать регулярное выражение, которое соответствует именно этому, следовательно, якорь $/ (из-за новой возможности частичного совпадения имен классов, добавленной в последних версиях). если он просто выполняет частичное совпадение, то он будет щелкать одно и то же снова и снова и ничего не добьется. - person Chuck van der Linden; 28.02.2012

Следуя подходу Чака, этот код также сначала перечисляет всех кандидатов, которым нужно щелкнуть, чтобы развернуться или что-то еще, а затем в однострочном цикл foreach, щелкает их снизу вверх по странице:

hitlist = $browser.divs(:class => /^expander$/)  # put all candidates into an array

hitlist.to_a.reverse.each {|r| r.click; sleep 0.1}  # loop in reverse

$browser.wait_until { $browser.divs(:class => /^expander$/).size < 1}  # Wait until they're all AJAXed out of existence

Строки 1 и 2 можно было бы даже сжать до просто $browser.divs(:class => /^expander$/).to_a.reverse.each {|r| r.click; sleep 0.1}, если вы не думаете, что промежуточная коллекция hitlist может быть удобной... но это неважно.

Несмотря на то, что код выглядит хорошо, моя главная проблема здесь, однако, заключается в очень серьезном снижении производительности при использовании локатора регулярных выражений, такого как /^expander$/, по-видимому, ... не то, что они говорят вам в документах! Не то, с чем я сталкивался до 0.5.3, когда стандартные локаторы классов в кавычках, такие как :class => "expander", сопоставлялись исключительно.

Один или два раза было бы простительно (в моем случае поиск div(:class => /^expander$/) занимает от 5 до 50 секунд на ‹100kB html-странице каждый раз), но меня особенно беспокоит строка 3: кто сколько раз останется {boolean test block} выполняется для опроса, если условие наконец выполнено?

person Marcos    schedule 01.03.2012
comment
Когда я пробовал, .reverse не было, но это достаточно легко исправить с помощью методов, унаследованных от ElementCollection #[], #each, #first, #initialize, #last, #length, #to_a Просто добавьте .to_a - person Marcos; 03.03.2012