While automating a test scenario for our client application, I got stuck with a StaleElementReference exception. After exhausting the other possible solutions of bypassing this exception, I searched on the internet for a way to resolve the error. This blog records the method I discovered that helped me in handling the exception. But before I share the solution, let me tell you a little bit about this exception.
What is StaleElementReference exception ?
A stale element is an element that is no more attached to the DOM of a webpage. The StaleElementReference exception occurs when code tries to perform an operation on a stale element.
For example,
readonly web_button = element(by.id(“sample_id”);
I want to perform a click operation on the above web element. The code is as follows:
/** * Method used to click on a button * @returns {Promise} */ async clickButton() { await this.web_button.click(); }
Let’s assume that just before the execution of the click() operation, the element reference is lost, possibly, due to a page refresh triggered by an API polling with timeouts. This will result in the StaleElementReference exception and the test will fail.
More cases that lead to StaleElementReference exception
If you are automating e2e tests for an angular application’s frontend, then you might face test failures due to StaleElementReference exception in the following cases:
- Automating front-end test using Protractor with Typescript
- The angular application has reactivity functionality
- The application has some APIs polling with timeouts
- Using protractor.map() with web elements from Protractor library
The solution I use more often
In most of the test scenarios, I make the code wait until the element appears on the DOM of a web page and then perform the operation. For example,
/** * Method used to click on a button * @returns {Promise} */ async clickButton() { let EC = protractor.ExpectedConditions; // Waits for element to be present on the DOM with 5s timeout await browser.wait(EC.presenceOf(web_button, 5000); await this.web_button.click(); }
Using retry() method to resolve StaleElementReference exception
In cases where the above approach doesn’t work (a few such cases listed above), we can use the retry () method from ts-retry-promise — npm package as follows:
- Importing retry function:
import { retry } from 'ts-retry-promise';
- Implementing the retry() method:
/** * Method to demonstrate use of retry method * @param webElement * @returns {string[]} */ async retryDemonstration(webElement: ElementFinder){ let element_text_array: string[]; await retry(async () => { element_text_array = await webElement .all(by.tagName('td')).map(async function (element) { return await element.getText(); }); }, { retries: 3 }); return element_text_array;
How does the retry() method work?
During the test execution, the operations written inside the retry() code block get reiterated as many times as mentioned in the retries parameter, which is 3 in the above example.
So, while performing the getText() operation for each web element, if an API is polled out and the element reference is lost, then instead of throwing the StaleElementReference exception, our code will retry with the fresh element reference. Hence the test will not fail.
If you have any more solutions that prevent test failures due to the StaleElementReference exception, please share them in the comments section.