fix selenium integration tests and introduce page object pattern

This commit is contained in:
Sebastian Sdorra
2016-07-31 20:21:11 +02:00
parent c4111ec73f
commit 5da8f5f052
12 changed files with 930 additions and 328 deletions

View File

@@ -326,13 +326,20 @@
<version>${selenium.version}</version> <version>${selenium.version}</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.seleniumhq.selenium</groupId> <groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-firefox-driver</artifactId> <artifactId>selenium-firefox-driver</artifactId>
<version>${selenium.version}</version> <version>${selenium.version}</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>htmlunit-driver</artifactId>
<version>2.21</version>
<scope>test</scope>
</dependency>
<dependency> <dependency>
<groupId>com.sun.jersey</groupId> <groupId>com.sun.jersey</groupId>
@@ -528,7 +535,7 @@
<scm.stage>DEVELOPMENT</scm.stage> <scm.stage>DEVELOPMENT</scm.stage>
<scm.home>target/scm-it</scm.home> <scm.home>target/scm-it</scm.home>
<environment.profile>default</environment.profile> <environment.profile>default</environment.profile>
<selenium.version>2.28.0</selenium.version> <selenium.version>2.53.1</selenium.version>
<enunciate.version>1.31</enunciate.version> <enunciate.version>1.31</enunciate.version>
<aether.version>1.13.1</aether.version> <aether.version>1.13.1</aether.version>
<wagon.version>1.0</wagon.version> <wagon.version>1.0</wagon.version>
@@ -688,6 +695,24 @@
<profile> <profile>
<id>selenium</id> <id>selenium</id>
<dependencies>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.3.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>xml-apis</groupId>
<artifactId>xml-apis</artifactId>
<version>1.4.01</version>
<scope>test</scope>
</dependency>
</dependencies>
<build> <build>
<plugins> <plugins>

View File

@@ -34,25 +34,27 @@ package sonia.scm.selenium;
//~--- non-JDK imports -------------------------------------------------------- //~--- non-JDK imports --------------------------------------------------------
import sonia.scm.selenium.page.Pages;
import sonia.scm.selenium.page.MainPage;
import sonia.scm.selenium.page.LoginPage;
import static org.junit.Assert.*;
import org.junit.Test; import org.junit.Test;
/** /**
* * Authentication related selenium integration tests.
*
* @author Sebastian Sdorra * @author Sebastian Sdorra
*/ */
public class AuthenticationITCase extends SeleniumTestBase public class AuthenticationITCase extends SeleniumITCaseBase {
{
/** /**
* Method description * Authenticates an user and call logout function.
*
*
* @throws Exception
*/ */
@Test @Test
public void testAuthentication() throws Exception public void testAuthentication() {
{ MainPage main = Pages.get(driver, LoginPage.class).login("scmadmin", "scmadmin");
login("scmadmin", "scmadmin"); assertEquals("scmadmin", main.getUserInfo());
logout(); main.logout();
} }
} }

View File

@@ -34,61 +34,55 @@ package sonia.scm.selenium;
//~--- non-JDK imports -------------------------------------------------------- //~--- non-JDK imports --------------------------------------------------------
import sonia.scm.selenium.page.Pages;
import sonia.scm.selenium.page.MainPage;
import sonia.scm.selenium.page.LoginPage;
import org.junit.After; import org.junit.After;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import static org.junit.Assert.*; import sonia.scm.repository.Repository;
/** /**
* * Repository related selenium integration tests.
*
* @author Sebastian Sdorra * @author Sebastian Sdorra
*/ */
public class RepositoryCRUDITCase extends SeleniumTestBase public class RepositoriesITCase extends SeleniumITCaseBase {
{
private MainPage main;
/** /**
* Method description * Authenticates admin user, before each test.
*
*/
@After
public void after()
{
logout();
}
/**
* Method description
*
*/
@Test
public void createRepository() throws InterruptedException
{
waitAndClick("#repositoryAddButton");
waitForPresence("input[name=name]").sendKeys("scm");
select("#x-form-el-repositoryType img").click();
waitAndClick("div.x-combo-list-item:nth-of-type(2)");
type("input[name=contact]", "scmadmin@scm-manager.org");
type("textarea[name=description]", "SCM-Manager");
waitAndClick("div.x-panel-btns button:nth-of-type(1)");
String name =
waitForPresence(
"div.x-grid3-row-selected div.x-grid3-col-name").getText();
assertEquals("scm", name);
waitAndClick("#repoRmButton button");
waitAndClick("div.x-window button:nth-of-type(1)");
}
/**
* Method description
*
*/ */
@Before @Before
public void login() public void login() {
{ main = Pages.get(driver, LoginPage.class)
login("scmadmin", "scmadmin"); .login("scmadmin", "scmadmin");
}
/**
* Creates, select and removes a repository.
*/
@Test
public void createRepository() {
Repository repository = new Repository();
repository.setName("scm");
repository.setType("git");
repository.setContact("scmadmin@scm-manager.org");
repository.setDescription("SCM-Manager");
main.repositories()
.add(repository)
.select(repository.getName())
.remove();
}
/**
* Logs the user out, after each test.
*/
@After
public void logout() {
main.logout();
} }
} }

View File

@@ -0,0 +1,71 @@
/**
* Copyright (c) 2014, Sebastian Sdorra
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. Neither the name of SCM-Manager; nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* http://bitbucket.org/sdorra/scm-manager
*
*/
package sonia.scm.selenium;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.firefox.FirefoxDriver;
/**
* Base class for selenium integration tests.
*
* @author Sebastian Sdorra
*/
public class SeleniumITCaseBase {
/**
* Selenium test driver.
*/
protected static WebDriver driver;
/**
* Setup selenium test driver.
*/
@BeforeClass
public static void setUpDriver() {
// DesiredCapabilities capa = DesiredCapabilities.chrome();
// capa.setBrowserName("firefox");
// capa.setPlatform(Platform.ANY);
// RemoteWebDriver driver = new RemoteWebDriver(new URL("http://localhost:4444/wd/hub"), capa);
driver = new FirefoxDriver();
driver.get("http://localhost:8082/scm/index.html");
}
/**
* Closes the selenium test driver.
*/
@AfterClass
public static void tearDownDriver() {
driver.close();
}
}

View File

@@ -1,271 +0,0 @@
/**
* Copyright (c) 2010, Sebastian Sdorra
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. Neither the name of SCM-Manager; nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* http://bitbucket.org/sdorra/scm-manager
*
*/
package sonia.scm.selenium;
//~--- non-JDK imports --------------------------------------------------------
import com.google.common.io.Files;
import org.junit.After;
import org.junit.Before;
import org.openqa.selenium.By;
import org.openqa.selenium.OutputType;
import org.openqa.selenium.TakesScreenshot;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static org.junit.Assert.*;
//~--- JDK imports ------------------------------------------------------------
import java.io.File;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
/**
*
* @author Sebastian Sdorra
*/
public class SeleniumTestBase
{
/**
* the logger for SeleniumTestBase
*/
private static final Logger logger =
LoggerFactory.getLogger(SeleniumTestBase.class);
//~--- methods --------------------------------------------------------------
/**
* Method description
*
*
* @throws Exception
*/
@After
public void tearDown() throws Exception
{
driver.quit();
}
//~--- set methods ----------------------------------------------------------
/**
* Method description
*
*
* @throws Exception
*/
@Before
public void setUp() throws Exception
{
driver = new FirefoxDriver();
baseUrl = "http://localhost:8082/scm/";
open("index.html");
}
//~--- methods --------------------------------------------------------------
/**
* Method description
*
*
* @param username
* @param password
*/
protected void login(String username, String password)
{
type("input[name=username]", username);
type("input[name=password]", password);
waitAndClick("#loginButton button");
String ue = waitForPresence("#scm-userinfo-tip").getText();
assertEquals(username, ue);
}
/**
* Method description
*
*/
protected void logout()
{
waitAndClick("#navLogout a");
}
/**
* Method description
*
*
* @param url
*/
protected void open(String url)
{
driver.get(baseUrl + url);
pause(500, TimeUnit.MILLISECONDS);
}
/**
* Method description
*
*
* @param value
* @param unit
*/
protected void pause(int value, TimeUnit unit)
{
driver.manage().timeouts().implicitlyWait(value, unit);
}
/**
* Method description
*
*
* @param target
*/
protected void screenshot(String target)
{
screenshot(new File(target));
}
/**
* Method description
*
*
* @param target
*/
protected void screenshot(File target)
{
try
{
File scrFile =
((TakesScreenshot) driver).getScreenshotAs(OutputType.FILE);
Files.copy(scrFile, target);
}
catch (IOException ex)
{
logger.error("could not create screenshot", ex);
}
}
/**
* Method description
*
*
* @param cssSelector
*
* @return
*/
protected WebElement select(String cssSelector)
{
WebElement element = driver.findElement(By.cssSelector(cssSelector));
assertNotNull(element);
return element;
}
/**
* Method description
*
*
* @param cssLocator
* @param value
*/
protected void type(String cssLocator, String value)
{
select(cssLocator).clear();
select(cssLocator).sendKeys(value);
}
/**
* Method description
*
*
* @param query
*/
protected void waitAndClick(String query)
{
waitToBeClickable(query).click();
}
/**
* Method description
*
*
* @param query
*
* @return
*/
protected WebElement waitForPresence(String query)
{
WebDriverWait wait = new WebDriverWait(driver, 5);
return wait.until(
ExpectedConditions.presenceOfElementLocated(By.cssSelector(query)));
}
/**
* Method description
*
*
* @param query
*
* @return
*/
protected WebElement waitToBeClickable(String query)
{
WebDriverWait wait = new WebDriverWait(driver, 5);
return wait.until(
ExpectedConditions.elementToBeClickable(By.cssSelector(query)));
}
//~--- fields ---------------------------------------------------------------
/** Field description */
protected WebDriver driver;
/** Field description */
private String baseUrl;
}

View File

@@ -0,0 +1,168 @@
/**
* Copyright (c) 2014, Sebastian Sdorra
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. Neither the name of SCM-Manager; nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* http://bitbucket.org/sdorra/scm-manager
*
*/
package sonia.scm.selenium.page;
import com.google.common.base.Throwables;
import com.google.common.io.Files;
import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.concurrent.TimeUnit;
import org.openqa.selenium.By;
import org.openqa.selenium.OutputType;
import org.openqa.selenium.TakesScreenshot;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;
/**
* Abstract selenium base page.
*
* @author Sebastian Sdorra
*
* @param <P> concrete page implementation
*/
public abstract class BasePage<P extends BasePage> {
/**
* Selenium test driver.
*/
protected final WebDriver driver;
/**
* Constructs a new base page.
*
* @param driver selenium test driver
*/
protected BasePage(WebDriver driver) {
this.driver = driver;
}
/**
* Performs a {@link Thread#sleep(long)} for the given timeout.
*
* @param time timeout
* @param unit time unit of timeout
*/
protected void sleep(long time, TimeUnit unit) {
try {
unit.sleep(time);
} catch (InterruptedException ex) {
throw Throwables.propagate(ex);
}
}
/**
* Wait for the element until it is clickable.
*
* @param by element selector
*
* @return web element
*/
protected WebElement waitToBeClickable(By by){
return waitToBeClickable(driver.findElement(by));
}
/**
* Waits for the element until it is clickable.
*
* @param element web element
*
* @return web element
*/
protected WebElement waitToBeClickable(WebElement element) {
WebDriverWait wait = new WebDriverWait(driver, 5);
return wait.until(ExpectedConditions.elementToBeClickable(element));
}
/**
* Waits until the element is present.
*
* @param by element locator
*
* @return web element
*/
protected WebElement waitFor(By by){
WebDriverWait wait = new WebDriverWait(driver, 1);
return wait.until(ExpectedConditions.presenceOfElementLocated(by));
}
/**
* Waits until the elements are present.
*
* @param by element selector
*
* @return list of web elements
*/
protected List<WebElement> waitForAll(By by){
WebDriverWait wait = new WebDriverWait(driver, 1);
return wait.until(ExpectedConditions.presenceOfAllElementsLocatedBy(by));
}
/**
* Creates a screenshot of the current browser content and stores it at the given path.
*
* @param target target file path
*
* @return {@code this}
*/
public P screenshot(String target) {
return screenshot(new File(target));
}
/**
* Creates a screenshot of the current browser content and stores it at the file.
*
* @param target target file
*
* @return {@code this}
*/
public P screenshot(File target) {
try {
File scrFile = ((TakesScreenshot) driver).getScreenshotAs(OutputType.FILE);
Files.copy(scrFile, target);
} catch (IOException ex) {
throw Throwables.propagate(ex);
}
return self();
}
/**
* Returns {@code this}.
*
* @return {@code this}
*/
protected abstract P self();
}

View File

@@ -0,0 +1,89 @@
/**
* Copyright (c) 2014, Sebastian Sdorra
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. Neither the name of SCM-Manager; nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* http://bitbucket.org/sdorra/scm-manager
*
*/
package sonia.scm.selenium.page;
import java.util.concurrent.TimeUnit;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
/**
* Page object for the scm-manager login page.
*
* @author Sebastian Sdorra
*/
public class LoginPage extends BasePage<LoginPage> {
@FindBy(css = "input[name=username]")
private WebElement usernameInput;
@FindBy(css = "input[name=password]")
private WebElement passwordInput;
@FindBy(css = "#loginButton button")
private WebElement loginButton;
/**
* Constructs a new page. This constructor should only be called from {@link Pages}.
*
* @param driver selenium test driver
*/
LoginPage(WebDriver driver) {
super(driver);
}
@Override
protected LoginPage self() {
return this;
}
/**
* Authenticates the user and returns the {@link MainPage}.
*
* @param username username
* @param password password
*
* @return {@link MainPage} after successful authentication
*/
public MainPage login(String username, String password) {
usernameInput.clear();
usernameInput.sendKeys(username);
passwordInput.clear();
passwordInput.sendKeys(password);
sleep(250, TimeUnit.MILLISECONDS);
waitToBeClickable(loginButton).click();
return Pages.get(driver, MainPage.class);
}
}

View File

@@ -0,0 +1,95 @@
/**
* Copyright (c) 2014, Sebastian Sdorra
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. Neither the name of SCM-Manager; nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* http://bitbucket.org/sdorra/scm-manager
*
*/
package sonia.scm.selenium.page;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
/**
* Page object for scm-manager's main page.
*
* @author Sebastian Sdorra
*/
public class MainPage extends BasePage<MainPage> {
@FindBy(css = "#navLogout a")
private WebElement logoutLink;
@FindBy(linkText = "Repositories")
private WebElement repositoriesLink;
@FindBy(css = "#scm-userinfo-tip")
private WebElement userInfoTip;
/**
* Constructs a new page. This constructor should only be called from {@link Pages}.
*
* @param driver selenium test driver
*/
MainPage(WebDriver driver) {
super(driver);
}
@Override
protected MainPage self() {
return this;
}
/**
* Returns the name of the current authenticated user from the user info tip.
*
* @return name of the current authenticated user
*/
public String getUserInfo(){
return userInfoTip.getText();
}
/**
* Navigates to the repositories page and returns the page object for this page.
*
* @return page object for repositories page
*/
public RepositoriesPage repositories(){
repositoriesLink.click();
return Pages.get(driver, RepositoriesPage.class);
}
/**
* Logs the current user out.
*
* @return page object for the login
*/
public LoginPage logout(){
waitToBeClickable(logoutLink).click();
return Pages.get(driver, LoginPage.class);
}
}

View File

@@ -0,0 +1,113 @@
/**
* Copyright (c) 2014, Sebastian Sdorra
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. Neither the name of SCM-Manager; nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* http://bitbucket.org/sdorra/scm-manager
*
*/
package sonia.scm.selenium.page;
import com.google.common.base.Throwables;
import com.google.common.collect.Lists;
import java.lang.reflect.Constructor;
import java.util.Arrays;
import java.util.List;
import org.openqa.selenium.By;
import org.openqa.selenium.SearchContext;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.PageFactory;
import org.openqa.selenium.support.pagefactory.DefaultElementLocatorFactory;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;
/**
* Helper class for selenium page objects.
*
* @author Sebastian Sdorra
*/
public final class Pages {
private Pages() {
}
/**
* Creates an instance of the given page object.
*
* @param <T> page object type
* @param driver selenium driver
* @param clazz page object type
* @param otherArguments other constructor arguments
*
* @return instance of page object
*/
public static <T extends BasePage> T get(WebDriver driver, Class<T> clazz, Object... otherArguments)
{
T page = null;
try {
List<Class<?>> argumentTypes = Lists.newArrayList();
argumentTypes.add(WebDriver.class);
for (Object argument : otherArguments) {
argumentTypes.add(argument.getClass());
}
List<Object> arguments = Lists.newArrayList();
arguments.add(driver);
arguments.addAll(Arrays.asList(otherArguments));
Constructor<T> constructor = clazz.getDeclaredConstructor(
argumentTypes.toArray(new Class<?>[argumentTypes.size()])
);
page = constructor.newInstance(arguments.toArray(new Object[arguments.size()]));
PageFactory.initElements(new DefaultElementLocatorFactory(new WaitingSearchContext(driver)), page);
} catch (Exception ex) {
throw Throwables.propagate(ex);
}
return page;
}
private static class WaitingSearchContext implements SearchContext {
private final WebDriver driver;
private final WebDriverWait wait;
private WaitingSearchContext(WebDriver driver) {
this.driver = driver;
this.wait = new WebDriverWait(driver, 1);
}
@Override
public List<WebElement> findElements(By by) {
return wait.until(ExpectedConditions.presenceOfAllElementsLocatedBy(by));
}
@Override
public WebElement findElement(By by) {
return wait.until(ExpectedConditions.presenceOfElementLocated(by));
}
}
}

View File

@@ -0,0 +1,136 @@
/**
* Copyright (c) 2014, Sebastian Sdorra
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. Neither the name of SCM-Manager; nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* http://bitbucket.org/sdorra/scm-manager
*
*/
package sonia.scm.selenium.page;
import com.google.common.base.Objects;
import java.util.List;
import org.openqa.selenium.By;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.NotFoundException;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import org.openqa.selenium.support.ui.WebDriverWait;
import sonia.scm.repository.Repository;
/**
* Page object for scm-manager's repository creation page.
*
* @author Sebastian Sdorra
*/
public class RepositoriesAddPage extends BasePage<RepositoriesAddPage> {
@FindBy(css = "input[name=name]")
private WebElement nameInput;
@FindBy(css = "input[name=contact]")
private WebElement contactInput;
@FindBy(css = "#x-form-el-repositoryType img")
private WebElement typeInput;
@FindBy(css = "textarea[name=description]")
private WebElement descriptionInput;
@FindBy(css = "div.x-panel-btns button:nth-of-type(1)")
private WebElement okButton;
private final RepositoriesPage repositoriesPage;
/**
* Constructs a new page. This constructor should only be called from {@link Pages}.
*
* @param driver selenium test driver
* @param repositoriesPage repositories page object
*/
RepositoriesAddPage(WebDriver driver, RepositoriesPage repositoriesPage) {
super(driver);
this.repositoriesPage = repositoriesPage;
}
@Override
protected RepositoriesAddPage self() {
return this;
}
/**
* Creates a new {@link Repository}.
*
* @param repository repository for creation
*
* @return repositories overview page
*/
public RepositoriesPage add(Repository repository) {
nameInput.sendKeys(repository.getName());
selectType(repository.getType());
contactInput.sendKeys(repository.getContact());
descriptionInput.sendKeys(repository.getDescription());
waitToBeClickable(okButton).click();
return repositoriesPage;
}
private void selectType(String type) {
typeInput.click();
String displayName = findDisplayName(type);
WebDriverWait wait = new WebDriverWait(driver, 1);
List<WebElement> elements = waitForAll(By.className("x-combo-list-item"));
WebElement typeElement = null;
for (WebElement te : elements){
if (te.getText().trim().equalsIgnoreCase(displayName)){
typeElement = te;
break;
}
}
if (typeElement == null){
throw new NotFoundException("could not find type element with type " + displayName);
}
typeElement.click();
}
private String findDisplayName(String type) {
String displayName = null;
if (driver instanceof JavascriptExecutor) {
// TODO seams not to work
String script = "Sonia.repository.getTypeByName('" + type + "').displayName;";
displayName = (String) ((JavascriptExecutor)driver).executeScript(script);
}
return Objects.firstNonNull(displayName, type);
}
}

View File

@@ -0,0 +1,103 @@
/**
* Copyright (c) 2014, Sebastian Sdorra
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. Neither the name of SCM-Manager; nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* http://bitbucket.org/sdorra/scm-manager
*
*/
package sonia.scm.selenium.page;
import java.util.List;
import java.util.Locale;
import org.openqa.selenium.By;
import org.openqa.selenium.NotFoundException;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import sonia.scm.repository.Repository;
/**
* Page object for scm-manager's repositories overview page.
*
* @author Sebastian Sdorra
*/
public class RepositoriesPage extends BasePage<RepositoriesPage> {
@FindBy(id = "repositoryAddButton")
private WebElement addButton;
/**
* Constructs a new page. This constructor should only be called from {@link Pages}.
*
* @param driver selenium test driver
*/
RepositoriesPage(WebDriver driver) {
super(driver);
}
@Override
protected RepositoriesPage self() {
return this;
}
/**
* Creates a new {@link Repository}.
*
* @param repository repository for creation
*
* @return {@link this}
*/
public RepositoriesPage add(Repository repository){
addButton.click();
RepositoriesAddPage addPage = Pages.get(driver, RepositoriesAddPage.class, this);
return addPage.add(repository);
}
/**
* Selects the repository with the given name and returns the detail page object for the selected repository.
*
* @param repositoryName name of the repository
*
* @return page object for selected repository
*/
public RepositoryPage select(String repositoryName){
WebElement repositoryNameColumn = null;
List<WebElement> elements = waitForAll(By.className("x-grid3-col-name"));
for (WebElement element : elements){
if (element.getText().trim().toLowerCase(Locale.ENGLISH).equals(repositoryName)){
repositoryNameColumn = element;
break;
}
}
if ( repositoryNameColumn == null ) {
throw new NotFoundException("could not find repository " + repositoryName);
}
return Pages.get(driver, RepositoryPage.class, this);
}
}

View File

@@ -0,0 +1,77 @@
/**
* Copyright (c) 2014, Sebastian Sdorra
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. Neither the name of SCM-Manager; nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* http://bitbucket.org/sdorra/scm-manager
*
*/
package sonia.scm.selenium.page;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
/**
* Page object for scm-manager's repository detail page.
*
* @author Sebastian Sdorra
*/
public class RepositoryPage extends BasePage<RepositoryPage> {
@FindBy(css = "#repoRmButton button")
private WebElement removeButton;
private final RepositoriesPage repositoriesPage;
/**
* Constructs a new page. This constructor should only be called from {@link Pages}.
*
* @param driver selenium test driver
* @param repositoriesPage repositories page object
*/
RepositoryPage(WebDriver driver, RepositoriesPage repositoriesPage) {
super(driver);
this.repositoriesPage = repositoriesPage;
}
@Override
protected RepositoryPage self() {
return this;
}
/**
* Removes the selected repository.
*
* @return repositories overview page object
*/
public RepositoriesPage remove(){
removeButton.click();
waitToBeClickable(By.cssSelector("div.x-window button:nth-of-type(1)")).click();
return repositoriesPage;
}
}