Reputation: 573
I've stuck with problem with Stale Element Reference Exception using protractor/jasmine2.
My spec:
var LoginPage = require('../pages/login_page.js');
var WelcomePage = require('../pages/welcome_page.js');
describe('Test -> testing something', function () {
var loginPage;
var EC = protractor.ExpectedConditions;
var waitTimeout = 10000;
function logIn() {
loginPage.setUser('user');
loginPage.setPassword('password');
loginPage.login();
}
beforeEach(function () {
browser.ignoreSynchronization = true;
loginPage = new LoginPage();
browser.wait(EC.presenceOf(loginPage.userLogin), waitTimeout);
logIn();
var welcomePage = new WelcomePage();
browser.wait(EC.visibilityOf(welcomePage.usersButton), waitTimeout);
welcomePage.usersButton.click();
});
The problem is that StaleElementReferenceException occurs randomly on the last line of beforeEach function when I want to click on the usersButton. No idea why ExpectedCondition do not work (have tried as well different EC like presenceOf, elementToBeClickable etc. but none solved problem).
See page with usersButton defined for reference:
'use strict';
var WelcomePage = function () {
};
WelcomePage.prototype = Object.create({}, {
usersButton: {
get: function () {
return element(by.css('#users a'));
}
}
});
module.exports = WelcomePage;
I think that some generic retry_mechanism would be needed to handle it, Anyone has similiar issue ?
Finally written function like that
var clickOn = function (element) {
browser.wait(EC.visibilityOf(element), waitTimeout).then(function() {
element.click();
});}
which is invoked like this:
clickOn(welcomePage.usersButton);
Update: Have tested it several times and when I am running tests on selenium grid I am still getting Stale Element exception on this exact element. So provided solution did not work ...
Failed: stale element reference: element is not attached to the page document (Session info: chrome=45.0.2454.93) (Driver info: chromedriver=2.19.346078 (6f1f0cde889532d48ce8242342d0b84f94b114a1),platform=Windows NT 6.1 SP1 x86_64) (WARNING: The server did not provide any stacktrace information) Command duration or timeout: 15 milliseconds For documentation on this error, please visit: http://seleniumhq.org/exceptions/stale_element_reference.html Build info: version: '2.47.1', revision: '411b314', time: '2015-07-30 03:03:16' System info: host: 'ITHFPC17', ip: '10.98.0.48', os.name: 'Windows 7', os.arch: 'x86', os.version: '6.1', java.version: '1.8.0_40' Driver info: org.openqa.selenium.chrome.ChromeDriver Capabilities [{applicationCacheEnabled=false, rotatable=false, mobileEmulationEnabled=false, chrome={userDataDir=C:\Users\SELENI~1\AppData\Local\Temp\scoped_dir2384_11396}, takesHeapSnapshot=true, databaseEnabled=false, handlesAlerts=true, hasTouchScreen=false, version=45.0.2454.93, platform=XP, browserConnectionEnabled=false, nativeEvents=true, acceptSslCerts=true, locationContextEnabled=true, webStorageEnabled=true, browserName=chrome, takesScreenshot=true, javascriptEnabled=true, cssSelectorsEnabled=true}] Session ID: 3244710644015ee170986333564ab806
Failed: Wait timed out after 10032ms
The next approach is to wait until element is present, afterwards if it's visible and afterwards if it's clickable... but it doesn't work as well. The strange thing is that on selenium grid everything works fine but when I am trying to run test locally then getting previously mentioned exception.
exports.clickOn = function (element) {
browser.wait(EC.presenceOf(element), waitTimeout).then(function () {
browser.wait(EC.visibilityOf(element), waitTimeout)
}).then(function () {
browser.wait(EC.elementToBeClickable(element), waitTimeout)
}).then(function () {
element.click();
});
};
Anyone has any idea how to deal with it ? ... I am stuck.
Upvotes: 3
Views: 4012
Reputation: 573
Finally I've managed this problem by adding 'not nice' simple retrying mechanism.
exports.clickOnElementWithWait = function (element, attempts) {
if (attempts == null) {
attempts = 10;
}
return element.click().then(function (found) {
}, function (err) {
if (attempts > 0) {
browser.sleep(100);
return exports.clickOnElementWithWait(element, attempts - 1);
} else {
throw err;
}
});
};
Update: Unfortunately it works as well randomly. The number of exceptions decreased but still ...
I have Java code which helped me when I've experienced it in pure selenium/junit and wondering if it's any free tool to convert it to javascript ? Anyone could help me ?
public class RetryMechanism {
private static final int DEFAULT_RETRY_NR = 20;
private static final long DEFAULT_WAIT_TIME_IN_MILLI = 1000;
private int numberOfRetries;
private int numberOfTriesLeft;
private static long timeToWait;
public RetryMechanism() {
this(DEFAULT_RETRY_NR, DEFAULT_WAIT_TIME_IN_MILLI);
}
public RetryMechanism(int numberOfRetries, long timeToWait) {
this.numberOfRetries = numberOfRetries;
numberOfTriesLeft = numberOfRetries;
this.timeToWait = timeToWait;
}
public boolean shouldRetry() {
return numberOfTriesLeft > 0;
}
public void errorOccurred(Throwable throwable) throws Exception {
numberOfTriesLeft--;
if (!shouldRetry()) {
throw new Exception("Retry Failed: Total " + numberOfRetries
+ " attempts made at interval " + getTimeToWait()
+ "ms.\n"
+ "Error message is : " + throwable.getMessage()
+ "\n"
+ "Caused by : " + throwable.getCause());
}
waitUntilNextTry();
}
public static long getTimeToWait() {
return timeToWait;
}
public static void waitUntilNextTry() {
try {
Thread.sleep(getTimeToWait());
} catch (InterruptedException ignored) {
}
}
public interface Action {
void doJob();
}
public static void retry(Action action) throws Exception {
RetryMechanism retry = new RetryMechanism();
while (retry.shouldRetry()) try {
action.doJob();
break;
} catch (Exception e) {
retry.errorOccurred(e);
}
}
}
Upvotes: 1
Reputation: 6962
Wait until the element is eligible to be clicked on by resolving the promise that the wait until elementToBeClickable()
function returns. Also make sure that your actions before click()
are completed, so that protractor can find the element as intended. Probably chaining all the actions to one another can be a good solution. That way the StaleElementReferenceException
error can be avoided. Here's how -
browser.wait(EC.presenceOf(loginPage.userLogin), waitTimeout).then(function(){
logIn();
}).then(function(){
var welcomePage = new WelcomePage();
}).then(function(){
browser.wait(EC.elementToBeClickable(welcomePage.usersButton), waitTimeout).then(function(){
welcomePage.usersButton.click();
});
});
Hope it helps.
Upvotes: 2