We all know that the navigation between pages is sometimes painful, and we need to write a lot lines of code to get to the right place. Using LoadableComponent is a better way to manage your navigation, especially if you need to go through welcome pages, login pages etc. in every single case of test. As simple as possible you just need to extend your Page Object classes with “LoadableComponent< T >” like this:

public class WelcomePage extends LoadableComponent<WelcomePage>

You need to override two methods:

@Override
	protected void isLoaded() throws Error{}

@Override
	protected void load() {}

The first one is for checking that the webpage is loaded correctly. You should place there assertions as many as you want, to test that the page is loaded in the right way. The second one is to load an exact page. We will demonstrate a simple usage of Page Object with LoadableComponent on the welcome page of Google.

import org.junit.Assert;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import org.openqa.selenium.support.PageFactory;
import org.openqa.selenium.support.ui.LoadableComponent;

public class WelcomePage extends LoadableComponent<WelcomePage>{

	private final WebDriver driver;
	
	@FindBy(css = "#gb_70")
	WebElement signInButton;
	
	@FindBy(css = "#gbqfq")
	WebElement searchField;
	
	@FindBy(css = "#gbqfba")
	WebElement searchButton;
	
	public WelcomePage(WebDriver driver){
		this.driver = driver;
		
		PageFactory.initElements(driver, this);
	}
	
	@Override
	protected void isLoaded() throws Error {
		String url = driver.getCurrentUrl();
		Assert.assertTrue("Not on the right page.", url.contains("google"));
	}

	@Override
	protected void load() {
		driver.get("https://www.google.com/");
	}
	
	public void clickSignIn(){
		signInButton.click();
	}
	
	public void fillSearchField(String keyword){
		searchField.sendKeys(keyword);
	}
	
	public void clickSearch(){
		searchButton.click();
	}
	
}

Here is a very simple test case for this PageObject:

@Test
	public void testWelcomPage(){
		WebDriver driver = new ChromeDriver();
		WelcomePage welcomePage = new WelcomePage(driver);
		welcomePage.get();
		welcomePage.fillSearchField("wedoqa");
	}

As you can see, with line welcomPage.get(); you get the Google welcome page loaded. The get() method also comes from LoadableCompontent class, but how does it work under the hood?
While you are going through the code in a debugging mode, you can notice that the call order of methods is weird. By calling get() method you will jump first to the isLoaded() method, than to load() method, and finally to isLoaded() method again. Here is a little code snippet from the source of LoadableComponent:

public T get() {
    try {
      isLoaded();
      return (T) this;
    } catch (Error e) {
      load();
    }

    isLoaded();

    return (T) this;
  }

At first call of isLoaded() method you will be redirected to the load() method, because Assert.assertTrue() will throw an AssertionError (There is no page loaded yet). Load() method should do its job, and at second call of isLoaded() method your assertion should pass, and your business logic can continue. If your page is already loaded the get() function will call just isLoaded() method.
That would be a simple usage of LoadableComponent, but there is some advanced practice also. In large percentage of the cases when you are writing a test you need to go through welcome pages, login pages, dashboard pages, etc. The best solution of this problem would be the concept called “nested components”. The benefit of this concept is that you can reduce your amount of code, while it still remains elegant.
Like at simple usage, we will use Google page to demonstrate this concept. Let’s say that we want to login to Google site, and go to Gmail to click on a Compose button. For this scenario we will use the familiar WelcomPage, and two others: LoginPage and EmailBoxPage. We need to define a hierarchy between these Page Objects, like this:

WelcomPage -> LoginPage -> EmailBoxPage

Here is the code of LoginPage.

import org.junit.Assert;
import org.openqa.selenium.By;
import org.openqa.selenium.NoSuchElementException;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.support.PageFactory;
import org.openqa.selenium.support.ui.LoadableComponent;

public class LoginPage extends LoadableComponent<LoginPage> {

	private final WebDriver driver;
	private final LoadableComponent<?> parent;
	private String username;
	private String password;
	
	public LoginPage(WebDriver driver, LoadableComponent<?> parent, String username, String password){
		this.driver = driver;
		this.parent = parent;
		this.username = username;
		this.password = password;
		
		PageFactory.initElements(driver, this);
	}
	
	@Override
	protected void isLoaded() throws Error {
		try{
		String account = driver.findElement(By.cssSelector("a.gb_8")).getAttribute("title");
		Assert.assertTrue("Not logged in with username: " + username, account.contains(username));
		}catch(NoSuchElementException ex){
			throw new AssertionError();
		}
	}

	@Override
	protected void load() {
		parent.get();
		driver.get("https://accounts.google.com/ServiceLogin?hl=en&continue=https://www.google.com/%3Fgws_rd%3Dcr%26ei%3DRIUtVKiyJun7ywPvrYGICQ");
		driver.findElement(By.cssSelector("#Email")).sendKeys(username);
		driver.findElement(By.cssSelector("#Passwd")).sendKeys(password);
		driver.findElement(By.cssSelector("#signIn")).click();
		driver.get("https://www.google.com/");
	}
}

Here is the code of EmailBoxPage.

import org.junit.Assert;
import org.openqa.selenium.By;
import org.openqa.selenium.NoSuchElementException;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import org.openqa.selenium.support.PageFactory;
import org.openqa.selenium.support.ui.LoadableComponent;

public class EmailBoxPage extends LoadableComponent<EmailBoxPage>{
	
	private final WebDriver driver;
	private final LoadableComponent<?> parent;
	
	@FindBy(css = ".T-I-KE")
	WebElement composeButton;
	
	public EmailBoxPage(WebDriver driver, LoadableComponent<?> parent){
		this.driver = driver;
		this.parent = parent;
		
		PageFactory.initElements(driver, this);
	}

	@Override
	protected void isLoaded() throws Error {
		try{
			Assert.assertTrue("Compose button is not displayed", driver.findElement(By.cssSelector(".T-I-KE")).isDisplayed());
		}catch(NoSuchElementException ex){
			throw new AssertionError();
		}
	}

	@Override
	protected void load() {
		parent.get();
		driver.get("https://mail.google.com/mail/u/0/?tab=wm&pli=1#inbox");
	}
	
	public void clickComposeButton(){
		composeButton.click();
	}
}

Here is the little test scenario to see the “nested component” concept in action:

import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;

import blog.pageobjects.EmailBoxPage;
import blog.pageobjects.LoginPage;
import blog.pageobjects.WelcomePage;

public class CreateMailTest {
	
	private static String username = "<your credentials>";
	private static String password = "<your credentials>";
	private WebDriver driver;
	private EmailBoxPage emailBoxPage;
	private WelcomePage welcomePage;
	private LoginPage loginPage;
	
	@Before
	public void before(){
		driver = new ChromeDriver();
		welcomePage = new WelcomePage(driver);
		loginPage = new LoginPage(driver, welcomePage, username, password);
		emailBoxPage = new EmailBoxPage(driver, loginPage);
	}
	
	@After
	public void after(){
		driver.close();
	}
	
	@Test
	public void testOpenMailBox(){
		emailBoxPage.get();
		emailBoxPage.clickComposeButton();
	}
}

You can notice that we have attribute LoadableComponent<?> parent in the classes LoginPage and EmailBoxPage. In before() method we create all of the Page Objects which is participating in our nested component concept. Going through our hierarchy, in constructor of LoginPage class we will place our WelcomPage as parent. In the constructor of EmailBoxPage we will place our LoginPage as parent. With this move we have the chain between these three objects. As you can see, we have the line parent.get() in EmailBoxPage’s load() function and in LoginPage’s load() function too. With these lines we can walk through our chained page objects with just one line of code: emailBoxPage.get().
This is a simple example of how you can reduce your amount of code, and make your tests more elegant. You can also add more levels in the hierarchy, and use more parameters in the constructor to improve functionality of your nested components.

Thanks for reading!

Similar Posts from the author:

One thought to “Simple and advanced usage of LoadableComponent”

  • ranjan

    Thanks …it’s really helpfull for me.

Comments are closed.