Implement global listener to pause code execution

Viewed 101

I have this code which is used to pause execution of Selenium code:

Boolean waitruntil = new WebDriverWait(driver, 20).until(ExpectedConditions.invisibilityOfElementLocated(By.xpath("//div[@class='ngx-loading-text center-center' and starts-with(., 'Loading')]")));

I have every test running into JUnit code. Do you know how I can implement a global listener which pauses code execution if loading bar is shown(detect using Xpath locator)

2 Answers

There is a WebDriverEventListener that can accomplish what you want. You could implement this listener such that before each command executed by selenium, the listener checks for the visibilityOfElementLocated(By.xpath("//div[@class='ngx-loading-text center-center' and starts-with(., 'Loading')]")));, and if so execute your WebDriverWait. Without seeing your code I'm not sure exactly how you'd implement this, but here's a resource with some good examples.

http://toolsqa.com/selenium-webdriver/event-listener/

One solution which is a bit more complex than using a listener would be to wrap your element with a proxy and perform the check whenever you hit the proxy.

Let the listeners listen to events and not change the test's flow,

First of all we need to create a class that will perform the check whenever you call the element:

public class ElementProxy implements InvocationHandler {

    private final WebElement element;

    public ElementProxy(WebElement element) {
        this.element = element;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        this.waitForPageLoad();
        return method.invoke(element, args);
    }

    private void waitForPageLoad() {
        WebDriver driver = ((WrapsDriver) element).getWrappedDriver();
        WebElement loadingBar = driver.findElement(By.xpath("//div[@class='ngx-loading-text center-center' and starts-with(., 'Loading')]"));
        new WebDriverWait(driver, 20).until(ExpectedConditions.invisibilityOfElement(loadingBar))
    }

    public static WebElement proxy(WebElement element) {
        ElementProxy proxy = new ElementProxy(element);
        WebElement wrappdElement = (WebElement) Proxy.newProxyInstance(ElementProxy.class.getClassLoader(),
                                                                       new Class[] { WebElement.class },
                                                                       proxy);
        return wrappdElement;
    }
    
}

Then you can call ElementProxy.proxy(element) which will return a new proxied element, similar to the behavior of PageFactory under the hood. Last thing is to wrap it under your own PageFactory so you can control the behavior of the elements:

public class MyPageFactory {
    
    public static <T> void initElements(WebDriver driver, T pageObject) {
        PageFactory.initElements(driver, pageObject);
        
        for (Field field : pageObject.getClass().getDeclaredFields()) {
            if (field.getType().equals(WebElement.class)) {
                boolean accessible = field.isAccessible();
                field.setAccessible(true);

                field.set(pageobject, ElementProxy.proxy((WebElement) field.get(pageObject)));
                field.setAccessible(accessible);
            }  
        }

    }
    
}

Now if you are in a web-page where the loading-bar is present, use your new MyPageFactory, which will perform the check automatically.

If you do not need to perform the check, use the regular PageFactory.

Reference: https://www.vinsguru.com/selenium-webdriver-how-to-handle-annoying-random-popup-alerts/

Related