Hi, I've recently upgraded protractor 2.1 from 1.6. I'm curious about using the element finders when testing the presence of (and disappearance) of an element.
Specifically, after the upgrade, I see spurious "stale element reference" exceptions within selenium when testing that a modal has been dismissed. The offending test is written like this:
browser.wait(function() {
return $('.modal').isPresent().then(function(present) {
return !present;
});
}, 3000);
Which results in:
Fatal error: 09:54:40.251 WARN - Exception thrown
org.openqa.selenium.StaleElementReferenceException: stale element reference: element is not attached to the page document
(Session info: chrome=43.0.2357.130)
(Driver info: chromedriver=2.15.322455 (ae8db840dac8d0c453355d3d922c91adfb61df8f),platform=Mac OS X 10.10.2 x86_64) (WARNING: The server did not provide any stacktrace information)
Command duration or timeout: 5 milliseconds
For documentation on this error, please visit: http://seleniumhq.org/exceptions/stale_element_reference.html
Build info: version: '2.45.0', revision: '5017cb8', time: '2015-02-26 23:59:50'
System info: host: 'the-internet.lan', ip: '192.168.1.139', os.name: 'Mac OS X', os.arch: 'x86_64', os.version: '10.10.2', java.version: '1.8.0_25'
Driver info: org.openqa.selenium.chrome.ChromeDriver
Capabilities [{applicationCacheEnabled=false, rotatable=false, mobileEmulationEnabled=false, chrome={userDataDir=/var/folders/hd/qyrygrsn157_44bl1m8t3dxw0000gq/T/.org.chromium.Chromium.8sEiw9}, takesHeapSnapshot=true, databaseEnabled=false, handlesAlerts=true, version=43.0.2357.130, platform=MAC, browserConnectionEnabled=false, nativeEvents=true, acceptSslCerts=true, locationContextEnabled=true, webStorageEnabled=true, browserName=chrome, takesScreenshot=true, javascriptEnabled=true, cssSelectorsEnabled=true}]
Session ID: 896a2dfd27f99dbceeca55d4a9dda44d
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:408)
at org.openqa.selenium.remote.ErrorHandler.createThrowable(ErrorHandler.java:204)
at org.openqa.selenium.remote.ErrorHandler.throwIfResponseFailed(ErrorHandler.java:156)
at org.openqa.selenium.remote.RemoteWebDriver.execute(RemoteWebDriver.java:599)
at org.openqa.selenium.remote.RemoteWebElement.execute(RemoteWebElement.java:268)
at org.openqa.selenium.remote.RemoteWebElement.isEnabled(RemoteWebElement.java:142)
at sun.reflect.GeneratedMethodAccessor24.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:483)
at org.openqa.selenium.support.events.EventFiringWebDriver$EventFiringWebElement$1.invoke(EventFiringWebDriver.java:331)
at com.sun.proxy.$Proxy3.isEnabled(Unknown Source)
at org.openqa.selenium.support.events.EventFiringWebDriver$EventFiringWebElement.isEnabled(EventFiringWebDriver.java:377)
at sun.reflect.GeneratedMethodAccessor24.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:483)
at org.openqa.selenium.remote.server.KnownElements$1.invoke(KnownElements.java:63)
at com.sun.proxy.$Proxy2.isEnabled(Unknown Source)
at org.openqa.selenium.remote.server.handler.GetElementEnabled.call(GetElementEnabled.java:30)
at org.openqa.selenium.remote.server.handler.GetElementEnabled.call(GetElementEnabled.java:1)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at org.openqa.selenium.remote.server.DefaultSession$1.run(DefaultSession.java:168)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
Bypassing the element finders and using the driver directly works:
browser.wait(function() {
return browser.driver.isElementPresent(by.css('.modal')).then(function(present) {
return !present;
});
}, 3000);
Is it user error on my part to use the element finders in this scenario? I was a bit surprised that they didn't work.
Thanks!!
A stale element is an element which is lo longer part of the page by the time you interact with it. So this looks like a race condition. Does that help?
Thanks for the quick response @sjelin. Yes, as you've stated there is a race condition. If the wait test begins before the modal has been removed from the DOM the stale element exception is thrown. I'm not confused about that.
I am confused about how to write a test that verifies the disappearance of an element from the DOM using the facilities provided by protractor. Right now it appears that this can't be done using the element finders and the only way to do this is to use webdriver directly.
Is this correct?
Ah, this does look like a bug actually. isPresent() should have been able to run without error. Does ExpectedConditions.presenceOf work for you?
@sjelin sorry for the delay. No, ExpectedConditions also results in intermittent stale element exceptions. That isn't surprising since ExpectedConditions.presenceOf simply forwards the call to the element finder.
I am used to get this error whenever interacting with elements controlled from the server side. I wrote an explanation on why this error is thrown on simple interactions as getText of an element: http://stackoverflow.com/questions/30671670/interacting-with-element-throw-stale-element-reference/30671671#30671671
So to check the elements present there should only be one WebDriver task, but since using an ElementFinder result in two tasks, I don't think there are any other way to do this.
I am also getting the "StaleElementReferenceError: stale element reference: element is not attached to the page document" when I click a button and a new page opens . I have tried 'presenceof' but it doesn't work me. Can you please suggests me ,how to overcome this problem?
isPresent had a bug where it would throw instead of returning false - this has been fixed in the latest release.
@juliemr @sjelin, this issue seems to me still present.
What you patched is the elementNotFound condition of the webdriver that happen when you look for an element and the element is not there.
the StaleElementReferenceError is instead an exception that the webdriver triggers when the the element is found but when the reference is effectively used the element disappeared from the page and is invalidated by the webdriver.
The proper test to be implemented is so not the one implemented in https://github.com/angular/protractor/commit/bd78dfc79b1435d124c994482df6879066079a4d but should be something like https://github.com/SeleniumHQ/selenium/blob/ef3b8e2e4152304d976fdcaae71155544aa4dd55/javascript/node/selenium-webdriver/test/stale_element_test.js#L49 where one get an element, change page and try to reuse the same element calling isPresent() with element not anymore on the page.
Most helpful comment
@juliemr @sjelin, this issue seems to me still present.
What you patched is the elementNotFound condition of the webdriver that happen when you look for an element and the element is not there.
the StaleElementReferenceError is instead an exception that the webdriver triggers when the the element is found but when the reference is effectively used the element disappeared from the page and is invalidated by the webdriver.
The proper test to be implemented is so not the one implemented in https://github.com/angular/protractor/commit/bd78dfc79b1435d124c994482df6879066079a4d but should be something like https://github.com/SeleniumHQ/selenium/blob/ef3b8e2e4152304d976fdcaae71155544aa4dd55/javascript/node/selenium-webdriver/test/stale_element_test.js#L49 where one get an element, change page and try to reuse the same element calling isPresent() with element not anymore on the page.