Before we get started let me say I have done my research on this matter, and I have seen the solutions posted here: stale element solution one, and I even came up with my own solution here: My temporary solution the problem with my solutions is that it does not work for all cases (particularly when dealing with long chains of .children()
The problem I have with "stale element solution one" is that it is not a very robust solution at all. It only works if you can put your Navigator element instantiation inside of the try catch, but if you do not have that instantiation, then this solution does no good. let me give an example of what I mean.
Lets say i have a Page class that looks something like this:
package interfaces
import geb.Page
import geb.navigator.Navigator
import org.openqa.selenium.By
class TabledPage extends Page {
static content ={
table {$(By.xpath("//tbody"))}
headers {$(By.xpath("//thead"))}
}
Navigator getAllRows(){
return table.children()
}
Navigator getRow(int index){
return table.children()[index]
}
Navigator getRow(String name){
return table.children().find{it.text().matches(~/.*\b${name}\b.*/)}
}
Navigator getColumn(Navigator row, int column){
return row.children()[column]
}
}
lets say that I have a method in my script that does what "stale element solution one" does (more or less). That looks like this:
def staleWraper(Closure c, args = null, attempts = 5){
def working = false
def trys = 0
while(!working){
try{
if(args){
return c(args)
}
else{
return c()
}
}
catch(StaleElementReferenceException se){
println("I caught me a stale element this many times: ${trys}")
if(trys>attempts){
working = true
throw se
}
else{
trys++
}
}
}
}
The way you call the above method is like this (using TabledPage as an example: staleWrapper(TabledPage.&getRow, 5) //grabs the 4th row of the table
and this works fine reason being, and this is important, the getRowmethod references an element that is in static content. When an static content element is reference, the Navigator is re-defined at run time. this is why this solution works for the getRow method. (table is re-instantiated inside of the try catch)
my problem and gripe with "stale element solution one" is that this type of implementation does not work for methods like getColumn this is because getColumn does not reference the static content itself. The Tabled page I am testing has javascript running on it that refreshes the DOM multiple times per second. so even if I use the staleWraper method it will always throw a stale element no matter how many attempts are made.
One solution to this is to add the columns as static content for the page, but I want to avoid that because it just doesn't flow with the way I have my whole project setup (I have many Page objects that implement methods in a similar way to TabledPage) If it were up to me, there would be a way to suppress the staleElelementExcpetions, but that is not an option either.
I am wondering if anyone here has a creative solution to Robustly (key word here) handle the StaleElementException, because I think looping over a try catch is already kinda hacky.
Well, I am not sure if my solution will solve your purpose. In my case I am using Page Pattern to design the tests so each method of Page class uses PageFactory to return instance of Page class. For example,
public class GoogleSearchPage {
// The element is now looked up using the name attribute
#FindBy(how = How.NAME, using = "q")
private WebElement searchBox;
public SearchResultPage searchFor(String text) {
// We continue using the element just as before
searchBox.sendKeys(text);
searchBox.submit();
// Return page instance of SearchResultPage class
return PageFactory.initElements(driver, SearchResultPage.class);
}
public GoogleSearchPage lookForAutoSuggestions(String text) {
// Do something
// Return page instance of itself as this method does not change the page
return PageFactory.initElements(driver, GoogleSearchPage.class);
}
}
The lookForAutoSuggestions may throw StaleElementException which is taken care by the returning the page instance. So if you are having page classes then ideally each page method should return an instance of page where user is supposed to land.
I ended up implementing something similar to what this guy gives as the "3rd option": Look at the answer (option 3)
I need to test it out some more but I have yet to get stale element since I implemented it this way (the key was to make my own WebElement class and then override the Navigator classes but use the NeverStaleWebElement object instead of the WebElement class
Related
I have a Spock class, that when run as a test suite, throws Unable to resolve iconRow as content for geb.Page, or as a property on its Navigator context. Is iconRow a class you forgot to import? unless I annotate my class with #Stepwise. However, I really don't want the test execution to stop on the first failure, which #Stepwise does.
I've tried writing (copy and pasting) my own extension using this post, but I still get these errors. It is using my extension, as I added some logging statements that were printed out to the console.
Here is one of my modules:
class IconRow extends Module {
static content = {
iconRow (required: false) {$("div.report-toolbar")}
}
}
And a page that uses it:
class Report extends SomeOtherPage {
static at = {$("div.grid-container").displayed}
static content = {
iconRow { module IconRow }
}
}
And a snippet of the test that is failing:
class MyFailingTest extends GebReportingSpec {
def setupSpec() {
via Dashboard
SomeClass.login("SourMonk", "myPassword")
assert page instanceof Dashboard
nav.goToReport("Some report name")
assert page instanceof Report
}
#Unroll
def "I work"() {
given:
at Report
expect:
this == that
where:
this << ["some list", "of values"]
that << anotherModule.someContent*.#id
}
#Unroll
def "I don't work"() {
given:
at Report
expect:
this == that
where:
this << ["some other", "list", "of values"]
that << iconRow.columnHeaders*.attr("innerText")*.toUpperCase()
}
}
When executed as a suite I work passes and I don't work fails because it cannot identify "iconRow" as content for the page. If I switch the order of the test cases, I don't work will pass and I work will fail. Alternatively, if I execute each test separately, they both pass.
What I have tried:
Adding/removing the required: true property from content in the modules
Prefixing the module name with the class, such as IconRow.iconRow
Defining my modules as static #Shared properties
Initialize the modules both in and outside of my setupSpec()
Making simple getter methods in each module's class that return the module, and referencing content such as IconRow.getIconRow().columnHeaders*.attr("innerText")*.toUpperCase()
Moving the contents of my setupSpec() into setup()
Adding autoClearCookies = false into my GebConfig.groovy
Making a #Shared Report report variable and prefix all modules with that such as report.iconRow
Very peculiar note about that last bullet point -- it magically resolves the modules that don't have the prefix -- so it won't resolve report.IconRow but will resolve just iconRow -- absolutely bizarre, because if I remove that variable the module that was just previously working suddenly can't be resolved again. I even tried declaring this variable and then not prefixing anything, and that did not work either.
Another problem that I keep banging my head against the wall with is that I'm also not sure of where the problem is. The error it throws leads me to believe that it's a project setup issue, but running each feature individually works fine, so it appears to be resolving the classes just fine.
On the other hand, perhaps it's an issue with the session and/or cookies? Although I have yet to see any official documentation on this, it seems to be the general consensus (from other posts and articles I've read) that only using #Stepwise will maintain your session between feature methods. If this is the case, why is my extension not working? It's pretty much a copy and paste of #Stepwise without the skipFeaturesAfterFirstFailingFeature method (I can post if needed), unless there is some other stuff going on behind the scenes with #Stepwise.
Apologies for the wall of text, but I've been trying to figure this out for about 6 hours now, so my brain is pretty fried.
Geb has special support for #Stepwise, if a spec is annotated with it it does not call resetBrowser() after each test, instead it is called after the spec is completed. See the code on github
So basically you need to change your setupSpec to setup so that it will be executed before each test.
Regarding your observation, if you just run a focused test the setupSpec is executed for that test and thus it passes. The problem arises, that the cleanup is invoked afterwards and resets the browser, breaking subsequent tests.
EDIT
I overlooked your usage of where blocks, everything in the where block needs to be statically (#Shared) available, so using instance level constructs won't work. Resetting the browser will also kill every reference so just getting it before wont work either. Basically, don't use Geb objects in where blocks!
Looking at your code however I don't see any reason to use data driven tests here.
This can be easily done with one assertion in a normal test
It is good practice for unit tests to just test one thing. Geb however, is not an unit test but an acceptance/frontend test. The problem here is that they are way slower than unit tests and it makes sense to combine sensible assertions into one test.
class MyFailingTest extends GebReportingSpec {
def setup() {
via Dashboard
SomeClass.login("SourMonk", "myPassword")
assert page instanceof Dashboard
nav.goToReport("Some report name")
assert page instanceof Report
}
def "I work"() {
given:
at Report
expect:
["some list", "of values"] == anotherModule.someContent*.#id
}
def "I don't work"() {
given:
at Report
expect:
["some other", "list", "of values"] == iconRow.columnHeaders*.attr("innerText")*.toUpperCase()
}
}
I need to wait for all elements to not be visible with an attribute 'id' that contains the word 'mask'. I have tried like this:
//*[contains(#id *= 'mask')]
However it is giving me an element not found exception.
I believe the syntax you're looking for is:
//*[contains(#id,'mask')]
I do something similar where I wait until the count is zero for classes that contain "BlockUI":
public static void waitForBlockUIToDisappear() {
// This function checks the entire currently-loaded web page for the
// presence of any web element of the
// class "blockUI" and loops until there are none. The presence of an
// element with this class does exactly
// what it implies: it blocks user input. If you get the error that a
// different web element would receive
// the click, for example, this is why and you'd need to call this
// method before doing a click. Generally,
// this function should be implemented before sending any input to a web
// page - click, sendkeys, select...
String blockUI = "//*[contains(#class,'blockUI')]";
while (true) {
if (driver.findElements(By.xpath(blockUI)).size() == 0)
break;
}
;
}
You should be able to alter that code to look for your id to contain your text.
You can use explicit wait in this case, as below.
WebDriverWait wait = new WebDriverWait(driver,15);
wait.until(ExpectedConditions.invisibilityOfAllElements(driver.findElements(By.xpath("//*[starts-with(#id,'mask')]"))));
In c# they were not implemented this method, but you can implement that.
Below is the url that can help you to write.
https://github.com/SeleniumHQ/selenium/blob/master/dotnet/src/support/UI/ExpectedConditions.cs#L140
My question is about phpunit+selenium usage.
The standard usage of this union is
class BlaBlaTest extends PHPUnit_Extensions_SeleniumTestCase
{... }
OR
class BlaBlaTest extends PHPUnit_Extensions_Selenium2TestCase
{...}
The first one (PHPUnit_Extensions_SeleniumTestCase) is not very convinient to use
(e.g. there is no such thing as $this->elements('xpath')).
Second(PHPUnit_Extensions_Selenium2TestCase) also has limited functionality
(e.g. there is no such functions as waitForPageToLoad() or clickAndWait(),
and using something like $this->timeouts()->implicitWait(10000) looks for me like
complete nonsense).
Is it possible to use the functional
PHPUnit_Extensions_SeleniumTestCase + PHPUnit_Extensions_Selenium2TestCase
in one test class?
Maybe smb knows good alternatives to phpunit+selenium?
Inspired by Dan I've written this for use in PHPUnit_Extensions_Selenium2TestCase and it seems to work ok:
/**
* #param string $id - DOM id
* #param int $wait - maximum (in seconds)
* #retrn element|false - false on time-out
*/
protected function waitForId($id, $wait=30) {
for ($i=0; $i <= $wait; $i++) {
try{
$x = $this->byId($id);
return $x;
}
catch (Exception $e) {
sleep(1);
}
}
return false;
}
Sorry for resurrecting this but I'd like to hopefully clear up some confusion for anyone stumbling across this.
You're saying that you wanted functionality from RC and WebDriver combined where there are workarounds to it, but I wouldn't recommend it. Firstly you'll need to understand the difference between both frameworks.
My brief definitions...
Selenium RC (PHPUnit_Extensions_SeleniumTestCase) is script oriented. By that I mean it will run your tests exactly how you expect the page to respond. This often will require more explicit commands such as the waitForPageToLoad() that you have mentioned when waiting for elements to appear and/or pages to loads.
Selenium WebDriver (PHPUnit_Extensions_Selenium2TestCase) uses a more native approach. It cuts off 'the middle-man' and runs your tests through your chosen browsers driver. Using the waitForPageToLoad() example, you wont need to explicitly put that wherever you open a page in your code because the WebDriver already knows when the page is loading and will resume the test when the page load request is complete.
If you need to define an implicit timeout in WebDriver, you will only need to place that in the setUp() method within a base Selenium class that will be extended in your test classes;
class BaseSelenium extends PHPUnit_Extensions_Selenium2TestCase {
protected function setUp() {
// Whatever else needs to be in here like setting
// the host url and port etc.
$this->setSeleniumServerRequestsTimeout( 100 ); // <- seconds
}
}
That will happily span across all of your tests and will timeout whenever a page takes longer than that to load.
Although I personally prefer WebDriver over RC (mainly because it's a lot faster!) there is a big difference between the methods available. Whenever I got stuck when recently converting a lot a RC tests to WebDriver I always turned to this first. It's a great reference to nearly every situation.
I hope that helps?
For functions such as waitForPageToLoad() and clickAndWait(), which are unavailable in Selenium2, you can reproduce those functions by using try catch blocks, in conjunction with implicit waits and explicit sleeps.
So, for a function like clickAndWait(), you can define what element you are waiting for, and then check for that element's existence for a set amount of seconds. If the element doesn't exist, the try catch block will stop the error from propagating. If the element does exist, you can continue. If the element doesn't exist after the set amount of time, then bubble up the error.
I would recommend using Selenium2 and then reproducing any functionality that you feel is missing from within your framework.
EXAMPLE:
def wait_for_present(element, retry = 10, seconds = 2)
for i in 0...retry
return true if element.present?
sleep(seconds)
end
return false
end
you can try use traits to extend two different classes http://php.net/manual/en/language.oop5.traits.php
class PHPUnit_Extensions_SeleniumTestCase{
...
}
change PHPUnit_Extensions_Selenium2TestCase class to trait:
trait PHPUnit_Extensions_Selenium2TestCase {
...
}
class blabla extends PHPUnit_Extensions_SeleniumTestCase {
use PHPUnit_Extensions_Selenium2TestCase;
your tests here..
}
The problem: I'm crashing when I want to render my incoming data which was retrieved asynchronously.
The app starts and displays some dialog boxes using XAML. Once the user fills in their data and clicks the login button, the XAML class has in instance of a worker class that does the HTTP stuff for me (asynchronously using IXMLHTTPRequest2). When the app has successfully logged in to the web server, my .then() block fires and I make a callback to my main xaml class to do some rendering of the assets.
I am always getting crashes in the delegate though (the main XAML class), which leads me to believe that I cannot use this approach (pure virtual class and callbacks) to update my UI. I think I am inadvertently trying to do something illegal from an incorrect thread which is a byproduct of the async calls.
Is there a better or different way that I should be notifying the main XAML class that it is time for it to update it's UI? I am coming from an iOS world where I could use NotificationCenter.
Now, I saw that Microsoft has it's own Delegate type of thing here: http://msdn.microsoft.com/en-us/library/windows/apps/hh755798.aspx
Do you think that if I used this approach instead of my own callbacks that it would no longer crash?
Let me know if you need more clarification or what not.
Here is the jist of the code:
public interface class ISmileServiceEvents
{
public: // required methods
virtual void UpdateUI(bool isValid) abstract;
};
// In main XAML.cpp which inherits from an ISmileServiceEvents
void buttonClick(...){
_myUser->LoginAndGetAssets(txtEmail->Text, txtPass->Password);
}
void UpdateUI(String^ data) // implements ISmileServiceEvents
{
// This is where I would render my assets if I could.
// Cannot legally do much here. Always crashes.
// Follow the rest of the code to get here.
}
// In MyUser.cpp
void LoginAndGetAssets(String^ email, String^ password){
Uri^ uri = ref new URI(MY_SERVER + "login.json");
String^ inJSON = "some json input data here"; // serialized email and password with other data
// make the HTTP request to login, then notify XAML that it has data to render.
_myService->HTTPPostAsync(uri, json).then([](String^ outputJson){
String^ assets = MyParser::Parse(outputJSON);
// The Login has returned and we have our json output data
if(_delegate)
{
_delegate->UpdateUI(assets);
}
});
}
// In MyService.cpp
task<String^> MyService::HTTPPostAsync(Uri^ uri, String^ json)
{
return _httpRequest.PostAsync(uri,
json->Data(),
_cancellationTokenSource.get_token()).then([this](task<std::wstring> response)
{
try
{
if(_httpRequest.GetStatusCode() != 200) SM_LOG_WARNING("Status code=", _httpRequest.GetStatusCode());
String^ j = ref new String(response.get().c_str());
return j;
}
catch (Exception^ ex) .......;
return ref new String(L"");
}, task_continuation_context::use_current());
}
Edit: BTW, the error I get when I go to update the UI is:
"An invalid parameter was passed to a function that considers invalid parameters fatal."
In this case I am just trying to execute in my callback is
txtBox->Text = data;
It appears you are updating the UI thread from the wrong context. You can use task_continuation_context::use_arbitrary() to allow you to update the UI. See the "Controlling the Execution Thread" example in this document (the discussion of marshaling is at the bottom).
So, it turns out that when you have a continuation, if you don't specify a context after the lambda function, that it defaults to use_arbitrary(). This is in contradiction to what I learned in an MS video.
However by adding use_currrent() to all of the .then blocks that have anything to do with the GUI, my error goes away and everything is able to render properly.
My GUI calls a service which generates some tasks and then calls to an HTTP class that does asynchronous stuff too. Way back in the HTTP classes I use use_arbitrary() so that it can run on secondary threads. This works fine. Just be sure to use use_current() on anything that has to do with the GUI.
Now that you have my answer, if you look at the original code you will see that it already contains use_current(). This is true, but I left out a wrapping function for simplicity of the example. That is where I needed to add use_current().
How are you finding out if jqGrid is loaded and ready to be used, via selenium.
Some details :
Im using C# driver
I have a method : new WebDriverWait(driver, new TimeSpan(0, 0, 0, 30)).Until(x => loadingdissapearedcondition) which im using to wait until Loading.. element is gone.
I also sometimes use this script :
private const string script = #"return ($('#{0}').jqGrid('getGridParam', 'reccount') !=x undefined) && ($('#{0}').jqGrid('getGridParam', 'reccount') != 0) && (!$('#load_{0}').is(':visible')) && (!$('#busyIcon').is(':visible'))";
private readonly string waitScript;
waitScript = string.Format(script, jqGridId);
public void WaitUntilLoadIconDissappears()
{
driver.WaitUntil(MAXWAIT, Wait);
}
public bool Wait()
{
var executeScript = ((IJavaScriptExecutor) driver).ExecuteScript(waitScript);
bool result;
bool tryParse = bool.TryParse(executeScript.SafeToString(), out result);
return tryParse && result;
}
to find if jqGrid has records and loading done.
I require something better - as even the above two does not make driver wait until load finishes, if we are using local data for jqGrid. Im also curious what is the best way, or at the minimum, how others are dealing with this problem.
I never used Selenium before, so I'm not sure that I understood your problem correctly. jqGrid will be first initialized and then (optionally) the data can be loaded from the server. During the initializing stage the original <table id="grid"></table> element will be converted to relatively complex HTML fragment which is the grid. At the end of the initialization the DOM element of the table (the $("#grid")[0]) will get the expando grid.
So you can use the test like
if ($("#grid")[0].grid) {
// grid is initialized
}
to determine that the grid is already initialized. jqGrid uses the same test internally (see here for example).
Here is solution for Java and jqgrid.
If grid data is not loaded yet then right pager has no value, so simply check its length. Methods such as isElementPresent, isDisplayed etc. seems not to work for grid right pager object. It's always present in page code while ajax, but text value is set when dynamic data is loaded.
public void waitForGridPagerRight(String gridName) throws Exception {
for (int second = 0;; second++) {
if (second >= 15) {
fail("Timeout.");
}
try {
if (driver
.findElement(By.id("pager_" + gridName + "_right"))
.getText().length() > 2)
break;
} catch (Exception e) {
}
Thread.sleep(1000);
}
}
Not sure why #Oleg's answer didn't work for me. It seemed like grid was being populated even before the ajax call was being made. It appears there's some change in newer versions maybe. It look like the last thing to happen in the ajax call is that the "loading" block is hidden, so instead I find that element and check it's display attribute a la:
def wait_for_jqgrid(self, t=20):
def check_jqgrid(driver):
#the last thing jqgrid does after the ajax call is to turn off the loading div
#so we find it and check to see if its display attribute is no longer set to 'none'
script = 'pid = $.jgrid.jqID($(".ui-jqgrid-btable").get(0).p.id); return $("#load_"+pid).get(0).style.display == "none";'
return driver.execute_script(script)
WebDriverWait(self.driver, t).until(check_jqgrid)
When the loading div is hidden, we are done. This is python but the same javascript bit should work in Java or whatever.