├── README.md ├── page-objects.iml ├── pom.xml └── src └── test └── java └── com └── example ├── pageobjects ├── BaseObjectPage.java ├── GitHubHomePage.java ├── GitHubJoinPage.java └── GitHubLoginPage.java ├── setup ├── CustomLoadableComponent.java ├── PageLoadHelper.java ├── SeleniumBaseTest.java └── SeleniumDriver.java └── test ├── GitHubJoinTest.java └── GitHubLoginTest.java /README.md: -------------------------------------------------------------------------------- 1 | Page Object Pattern with Loadable Component 2 | ========================================== 3 | This project is an example of building a test automation framework using WebDriver, Java, TestNG, Maven with InteliJ (or Eclipse). 4 | 5 | There are a several of key concepts demonstrated in this project: 6 | 7 | - Page Objects Pattern 8 | - Abstracting the test setup into a SeleniumBaseTest class 9 | - Implementing Custom LoadableComponent 10 | - Organized Selenium WebDriver setup 11 | - Using TestNG and Maven to run the tests 12 | 13 | Please contact me if you have any questions or suggestions. 14 | -------------------------------------------------------------------------------- /page-objects.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | page-objects-example 7 | page-objects-example 8 | 1.0 9 | 10 | 11 | UTF-8 12 | 13 | 14 | 15 | 16 | org.seleniumhq.selenium 17 | selenium-java 18 | 2.53.0 19 | 20 | 21 | org.testng 22 | testng 23 | 6.9.10 24 | test 25 | 26 | 27 | org.seleniumhq.selenium 28 | selenium-server 29 | 2.53.0 30 | 31 | 32 | org.seleniumhq.selenium 33 | selenium-firefox-driver 34 | 2.53.0 35 | 36 | 37 | 38 | 39 | 40 | org.apache.maven.plugins 41 | maven-compiler-plugin 42 | 43 | 1.6 44 | 1.6 45 | 46 | 47 | 48 | 49 | org.apache.maven.plugins 50 | maven-resources-plugin 51 | 2.6 52 | 53 | 54 | UTF-8 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /src/test/java/com/example/pageobjects/BaseObjectPage.java: -------------------------------------------------------------------------------- 1 | package com.example.pageobjects; 2 | 3 | import static com.example.setup.SeleniumDriver.getDriver; 4 | 5 | import com.example.setup.CustomLoadableComponent; 6 | import org.openqa.selenium.By; 7 | import org.openqa.selenium.NoSuchElementException; 8 | import org.openqa.selenium.WebDriver; 9 | import org.openqa.selenium.WebElement; 10 | import org.openqa.selenium.support.PageFactory; 11 | 12 | 13 | /** 14 | * Created by Sargis on 4/3/16. 15 | */ 16 | 17 | public abstract class BaseObjectPage> extends CustomLoadableComponent { 18 | private WebDriver driver; 19 | private static final String BASE_URL = "https://github.com"; 20 | 21 | public BaseObjectPage(WebDriver driver) { 22 | this.driver = driver; 23 | } 24 | 25 | public T openPage(Class clazz) { 26 | T page = PageFactory.initElements(getDriver(), clazz); 27 | getDriver().get(BASE_URL + getPageUrl()); 28 | return page.get(); 29 | } 30 | 31 | public abstract String getPageUrl(); 32 | 33 | public void open(String url) { 34 | driver.get(url); 35 | } 36 | 37 | public WebElement find(By locator) { 38 | return driver.findElement(locator); 39 | } 40 | 41 | public void type(By inputLocator, String text) { 42 | find(inputLocator).sendKeys(text); 43 | } 44 | 45 | public void type(WebElement input, String text) { 46 | input.sendKeys(text); 47 | } 48 | 49 | public void click(By locator) { 50 | find(locator).click(); 51 | } 52 | 53 | public void click(WebElement element) { 54 | element.click(); 55 | } 56 | 57 | public boolean isElementDisplayed(WebElement element) { 58 | try { 59 | return element.isDisplayed(); 60 | } catch (NoSuchElementException e) { 61 | return false; 62 | } 63 | } 64 | } -------------------------------------------------------------------------------- /src/test/java/com/example/pageobjects/GitHubHomePage.java: -------------------------------------------------------------------------------- 1 | package com.example.pageobjects; 2 | 3 | import com.example.setup.PageLoadHelper; 4 | import org.openqa.selenium.By; 5 | 6 | import static com.example.setup.SeleniumDriver.getDriver; 7 | 8 | /** 9 | * Page object GitHub home page. 10 | * 11 | * Created by Sargis on 4/3/16. 12 | */ 13 | 14 | public class GitHubHomePage extends BaseObjectPage { 15 | 16 | 17 | public GitHubHomePage() { 18 | super(getDriver()); 19 | } 20 | 21 | @Override 22 | public String getPageUrl() { 23 | return ""; 24 | } 25 | 26 | public GitHubLoginPage goToLoginPage() { 27 | return new GitHubLoginPage().openPage(GitHubLoginPage.class); 28 | } 29 | 30 | public GitHubHomePage open() { 31 | return new GitHubHomePage().openPage(GitHubHomePage.class); 32 | } 33 | 34 | @Override 35 | protected void load() { 36 | 37 | } 38 | 39 | @Override 40 | protected void isLoaded() throws Error { 41 | PageLoadHelper.isLoaded(). 42 | isElementIsVisible(By.cssSelector("input[name='user[login]']")). 43 | isElementIsClickable(By.cssSelector("input[name='user[login]']")); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/test/java/com/example/pageobjects/GitHubJoinPage.java: -------------------------------------------------------------------------------- 1 | package com.example.pageobjects; 2 | 3 | import com.example.setup.PageLoadHelper; 4 | import org.openqa.selenium.By; 5 | import org.openqa.selenium.WebElement; 6 | import org.openqa.selenium.support.FindBy; 7 | 8 | import static com.example.setup.SeleniumDriver.getDriver; 9 | 10 | /** 11 | * Created by sargis on 6/19/16. 12 | */ 13 | public class GitHubJoinPage extends BaseObjectPage { 14 | @FindBy(id = "user_login") 15 | WebElement usernameField; 16 | 17 | @FindBy(id = "user_email") 18 | WebElement emailField; 19 | 20 | @FindBy(id = "user_password") 21 | WebElement passwordField; 22 | 23 | @FindBy(className = "flash-error") 24 | WebElement errorBox; 25 | 26 | @FindBy(id = "signup_button") 27 | WebElement joinButton; 28 | 29 | 30 | public GitHubJoinPage() { 31 | super(getDriver()); 32 | } 33 | 34 | public void registerNewUser(String username, String email, String password) { 35 | type(usernameField, username); 36 | type(emailField, email); 37 | type(passwordField, password); 38 | click(joinButton); 39 | } 40 | 41 | public boolean isLoginError() { 42 | return isElementDisplayed(errorBox); 43 | } 44 | 45 | @Override 46 | public String getPageUrl() { 47 | return "/join"; 48 | } 49 | 50 | @Override 51 | protected void load() { 52 | 53 | } 54 | 55 | 56 | @Override 57 | protected void isLoaded() throws Error { 58 | PageLoadHelper.isLoaded(). 59 | isElementIsVisible(By.cssSelector("input[id='user_login']")). 60 | isElementIsClickable(By.cssSelector("input[id='user_login']")); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/test/java/com/example/pageobjects/GitHubLoginPage.java: -------------------------------------------------------------------------------- 1 | package com.example.pageobjects; 2 | 3 | import com.example.setup.PageLoadHelper; 4 | import org.openqa.selenium.By; 5 | import org.openqa.selenium.WebElement; 6 | import org.openqa.selenium.support.FindBy; 7 | 8 | import static com.example.setup.SeleniumDriver.getDriver; 9 | 10 | /** 11 | * Page object GitHub login page 12 | * 13 | * Created by Sargis on 4/3/16. 14 | */ 15 | public class GitHubLoginPage extends BaseObjectPage { 16 | 17 | @FindBy(id = "login_field") 18 | WebElement loginField; 19 | 20 | @FindBy(name = "password") 21 | WebElement passwordField; 22 | 23 | @FindBy(name = "commit") 24 | WebElement commitButton; 25 | 26 | @FindBy(className = "flash-error") 27 | WebElement errorBox; 28 | 29 | public GitHubLoginPage() { 30 | super(getDriver()); 31 | } 32 | 33 | @Override 34 | public String getPageUrl() { 35 | return "/login"; 36 | } 37 | 38 | public void login(String login, String password) { 39 | type(loginField, login); 40 | type(passwordField, password); 41 | click(commitButton); 42 | } 43 | 44 | public boolean isLoginError() { 45 | return isElementDisplayed(errorBox); 46 | } 47 | 48 | public String getErrorMessage() { 49 | return errorBox.getText(); 50 | } 51 | 52 | @Override 53 | protected void load() { 54 | 55 | } 56 | 57 | @Override 58 | protected void isLoaded() throws Error { 59 | PageLoadHelper.isLoaded(). 60 | isElementIsVisible(By.cssSelector("#login_field")). 61 | isElementIsClickable(By.cssSelector("#login_field")); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/test/java/com/example/setup/CustomLoadableComponent.java: -------------------------------------------------------------------------------- 1 | package com.example.setup; 2 | 3 | import org.openqa.selenium.WebDriver; 4 | import org.openqa.selenium.support.ui.ExpectedCondition; 5 | import org.openqa.selenium.support.ui.FluentWait; 6 | import org.openqa.selenium.support.ui.Wait; 7 | 8 | import java.util.concurrent.TimeUnit; 9 | 10 | import static com.example.setup.SeleniumDriver.getDriver; 11 | 12 | /** 13 | * Custom Loadable Component 14 | * 15 | * Created by sargis on 4/3/16. 16 | */ 17 | public abstract class CustomLoadableComponent> { 18 | private WebDriver driver; 19 | 20 | private static final int LOAD_TIMEOUT = 30; 21 | private static final int REFRESH_RATE = 2; 22 | 23 | @SuppressWarnings("unchecked") 24 | public T get() { 25 | try { 26 | isLoaded(); 27 | return (T) this; 28 | } catch (Error e) { 29 | // This is the extra line of code 30 | System.out.println("Error encountered during page load: " + e.getMessage()); 31 | load(); 32 | } 33 | 34 | isLoaded(); 35 | 36 | return (T) this; 37 | } 38 | 39 | protected abstract void load(); 40 | 41 | protected abstract void isLoaded() throws Error; 42 | 43 | protected void waitForPageToLoad(ExpectedCondition pageLoadCondition) { 44 | Wait wait = new FluentWait(getDriver()) 45 | .withTimeout(LOAD_TIMEOUT, TimeUnit.SECONDS) 46 | .pollingEvery(REFRESH_RATE, TimeUnit.SECONDS); 47 | 48 | wait.until(pageLoadCondition); 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /src/test/java/com/example/setup/PageLoadHelper.java: -------------------------------------------------------------------------------- 1 | package com.example.setup; 2 | 3 | import org.openqa.selenium.By; 4 | import org.openqa.selenium.WebDriverException; 5 | import org.openqa.selenium.support.ui.ExpectedConditions; 6 | import org.openqa.selenium.support.ui.WebDriverWait; 7 | 8 | import static com.example.setup.SeleniumDriver.getDriver; 9 | 10 | /** 11 | * PageLoadHelper 12 | * 13 | * Created by Sargis on 4/3/16. 14 | */ 15 | public class PageLoadHelper { 16 | public static PageLoadHelper isLoaded() { 17 | PageLoadHelper loadHelper = new PageLoadHelper(); 18 | return loadHelper; 19 | } 20 | 21 | 22 | public PageLoadHelper isElementIsClickable(By by) { 23 | try { 24 | new WebDriverWait(getDriver(), 10).until(ExpectedConditions.elementToBeClickable(by)); 25 | return this; 26 | } catch (WebDriverException e) { 27 | throw new Error("Element is not clickable"); 28 | } 29 | } 30 | 31 | public PageLoadHelper isElementIsVisible(By by) { 32 | try { 33 | new WebDriverWait(getDriver(), 10).until(ExpectedConditions.visibilityOfElementLocated(by)); 34 | return this; 35 | } catch (WebDriverException e) { 36 | throw new Error("Element is not visible"); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/test/java/com/example/setup/SeleniumBaseTest.java: -------------------------------------------------------------------------------- 1 | package com.example.setup; 2 | 3 | import org.testng.annotations.AfterClass; 4 | 5 | import static com.example.setup.SeleniumDriver.getDriver; 6 | 7 | /** 8 | * SeleniumBaseTest 9 | * 10 | * Created by Sargis on 4/3/16. 11 | */ 12 | public class SeleniumBaseTest { 13 | @AfterClass 14 | public static void tearDown() { 15 | getDriver().close(); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/test/java/com/example/setup/SeleniumDriver.java: -------------------------------------------------------------------------------- 1 | package com.example.setup; 2 | 3 | import org.openqa.selenium.WebDriver; 4 | import org.openqa.selenium.firefox.FirefoxDriver; 5 | 6 | /** 7 | * Selenium driver wrapper 8 | * 9 | * Created by Sargis on 4/3/16. 10 | */ 11 | public class SeleniumDriver { 12 | 13 | static WebDriver driver; 14 | 15 | public static WebDriver getDriver() { 16 | if (driver == null) { 17 | driver = new FirefoxDriver(); 18 | } 19 | return driver; 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /src/test/java/com/example/test/GitHubJoinTest.java: -------------------------------------------------------------------------------- 1 | package com.example.test; 2 | 3 | import com.example.pageobjects.GitHubJoinPage; 4 | import com.example.setup.SeleniumBaseTest; 5 | import org.testng.annotations.Test; 6 | 7 | import static org.testng.Assert.assertTrue; 8 | 9 | /** 10 | * Created by sargis on 6/19/16. 11 | */ 12 | public class GitHubJoinTest extends SeleniumBaseTest { 13 | @Test 14 | public void usernameExistTest() { 15 | //navigate to login page 16 | GitHubJoinPage joinPage = new GitHubJoinPage().openPage(GitHubJoinPage.class); 17 | 18 | //try to login 19 | joinPage.registerNewUser("user", "emial@mail.com", "password"); 20 | assertTrue(joinPage.isLoginError(), "Error message should be visible!"); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/test/java/com/example/test/GitHubLoginTest.java: -------------------------------------------------------------------------------- 1 | package com.example.test; 2 | 3 | import static org.testng.Assert.assertEquals; 4 | import static org.testng.Assert.assertTrue; 5 | 6 | import com.example.pageobjects.GitHubHomePage; 7 | import com.example.pageobjects.GitHubLoginPage; 8 | import com.example.setup.SeleniumBaseTest; 9 | import org.testng.annotations.Test; 10 | 11 | 12 | public class GitHubLoginTest extends SeleniumBaseTest { 13 | 14 | @Test 15 | public void shouldNotLoginWithWrongCredentials() { 16 | //navigate to login page 17 | GitHubHomePage homePage = new GitHubHomePage().open(); 18 | GitHubLoginPage loginPage = homePage.goToLoginPage(); 19 | 20 | //try to login 21 | loginPage.login("user", "password"); 22 | 23 | //assert there is an error message 24 | assertTrue(loginPage.isLoginError(), "Error message was not displayed!"); 25 | assertEquals(loginPage.getErrorMessage(), "Incorrect username or password.", "Error message was incorrect!"); 26 | } 27 | 28 | } 29 | --------------------------------------------------------------------------------