Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Waiting for render events with Selenium and React

I am getting started with using Selenium to test a React application that I have built.

In most simple JavaScript applications it is trivial to wire up a test where one uses the WebDriverWait class to wait for some event to signal that the test can proceed to the next step.

For example:

using (IWebDriver driver = new ChromeDriver())
{
    driver.Navigate().GoToUrl($"{Properties.Settings.Default.ApplicationUrl}/Dashboard");

    var wait = new WebDriverWait(driver, TimeSpan.FromSeconds(10));
    wait.Until(d => d.Title.StartsWith("dashboard", StringComparison.OrdinalIgnoreCase));

    //Can do next thing....

    Assert.IsTrue(true);
}

However things get a bit more complicated if we want to test a React application. Given that each component in your React application will each individually invoke their own lifecycle events, in their own time, it is extremely difficult to know when a given control has completed its Render() lifecycle.

I have played with a simple solution like:

//Add something like this to each React component
componentDidMount() {
    if (!window.document.hasMyComponentMounted)
        window.document.hasMyComponentMounted = true;
}

and then I can do something like this in my tests:

using (IWebDriver driver = new ChromeDriver())
{
    IJavaScriptExecutor js = (IJavaScriptExecutor)driver;

    driver.Navigate().GoToUrl($"{Properties.Settings.Default.ApplicationUrl}/Dashboard");

    var wait = new WebDriverWait(driver, TimeSpan.FromSeconds(10));
    wait.Until(d => js.ExecuteScript("return window.document.hasHomeMounted"));

    //Can do next thing....

    Assert.IsTrue(true);
}

but this seems like a kinda sucky solution.

Has anyone encountered the same problem or know of potential workarounds (except for just using Thread.Sleep())?

like image 718
Maxim Gershkovich Avatar asked Jun 14 '19 17:06

Maxim Gershkovich


People also ask

Can Selenium automate Reactjs application?

Selenium and React are the two popular tools, unique in their own ways and common in software development and testing circles. React is a JavaScript library meant to create interactive user interfaces. On the other hand, Selenium is used to perform automation testing on such user interfaces and web pages.

How do you wait for an element to appear in Selenium?

Explicit Wait in SeleniumBy using the Explicit Wait command, the WebDriver is directed to wait until a certain condition occurs before proceeding with executing the code. Setting Explicit Wait is important in cases where there are certain elements that naturally take more time to load.

How do you implement explicit wait?

Syntax of Explicit wait in selenium webdriver // Create object of WebDriverWait class WebDriverWait wait=new WebDriverWait(driver,20); // Wait till the element is not visible WebElement element=wait. until(ExpectedConditions. visibilityOfElementLocated(By.

How do you check if page is completely loaded in Selenium?

We can get Selenium to recognize that a page is loaded. We can set the implicit wait for this purpose. It shall make the driver to wait for a specific amount of time for an element to be available after page loaded.


1 Answers

I have written extensive automation for a React.js application, and as you mentioned, it was a very difficult problem waiting for components to render.

There is no concrete solution that can fix every issue with this. The solution you mentioned, to wait for components to mount, is a good one. You can make it cleaner by wrapping it in with WebDriver calls so that they automatically wait for the components to mount, and you do not have to specify that code in your test cases.

Here's an idea:

public static class WebDriverExtensions()
{
    public static void WaitForComponents(this IWebDriver driver)
    {
        var wait = new WebDriverWait(driver, TimeSpan.FromSeconds(10));
        wait.Until(d => js.ExecuteScript("return window.document.hasHomeMounted"));
    }

    public static void GoToUrl(this IWebDriver driver, string url)
    {
        driver.Navigate().GoToUrl(url);
        driver.WaitForComponents();
    }
}

Then, your test case will look like:

using WebDriverExtensions;

using (IWebDriver driver = new ChromeDriver())
{
    IJavaScriptExecutor js = (IJavaScriptExecutor)driver;

    driver.GoToUrl($"{Properties.Settings.Default.ApplicationUrl}/Dashboard");


    //Can do next thing....

    Assert.IsTrue(true);
}

If the component you are checking is constantly changing, i.e. hasHomeMounted only works on the Home page, the solution is a little uglier. You could try something that takes a component name as a parameter to check if it is mounted:

    public static void WaitForComponent(this IWebDriver driver, string componentName)
    {
        var wait = new WebDriverWait(driver, TimeSpan.FromSeconds(10));
        wait.Until(d => js.ExecuteScript($"return window.document.has{componentName}Mounted"));
    }

As I mentioned, there is no catch-all way to accomplish this, and using Thread.Sleep() everywhere is not advisable. Your solution which checks the react component mounting is a good solution.

If you do not like the above solution, here's what I specifically used to handle most of my React UI testing:

public static void WaitAndClick(this IWebDriver driver, By by)
{
     var wait = new WebDriverWait(driver, TimeSpan.FromSeconds(10));
     wait.Until(ExpectedConditions.ElementToBeClickable(by));

     driver.FindElement(by).Click();
}

My issues with automating React UI involved elements taking too long to load. Most of the page would load, but the component I was trying to click would not exist sometimes. So I wrapped Click() in a WaitAndClick() method, to ensure the element is clickable before I try to perform the click. This worked for most of my cases.

like image 185
CEH Avatar answered Oct 20 '22 00:10

CEH