├── .gitignore ├── deploy ├── src ├── test │ ├── resources │ │ ├── testng-selenium.properties │ │ └── testng.xml │ └── java │ │ └── com │ │ └── github │ │ └── jsdevel │ │ └── testng │ │ └── selenium │ │ └── samples │ │ ├── GoogleSearchResultsPage.java │ │ ├── SamplePageFactory.java │ │ ├── GoogleHomePage.java │ │ └── SampleSuiteITest.java └── main │ ├── java │ └── com │ │ └── github │ │ └── jsdevel │ │ └── testng │ │ └── selenium │ │ ├── package-info.java │ │ ├── exceptions │ │ ├── package-info.java │ │ ├── PageInstantiationException.java │ │ ├── PageInitializationException.java │ │ └── MissingPageFactoryException.java │ │ ├── config │ │ ├── package-info.java │ │ ├── ConfigDefaults.java │ │ ├── ConfigOptions.java │ │ ├── PropertiesConfig.java │ │ └── Config.java │ │ ├── annotations │ │ ├── driverconfig │ │ │ ├── package-info.java │ │ │ └── UserAgent.java │ │ ├── drivers │ │ │ ├── package-info.java │ │ │ ├── Chrome.java │ │ │ ├── Firefox.java │ │ │ ├── PhantomJS.java │ │ │ └── InternetExplorer.java │ │ └── screensizes │ │ │ ├── package-info.java │ │ │ ├── Phone.java │ │ │ ├── Tablet.java │ │ │ ├── Desktop.java │ │ │ └── LargeDesktop.java │ │ ├── PageFactory.java │ │ ├── Page.java │ │ ├── TestNGSeleniumLogger.java │ │ ├── MethodContext.java │ │ ├── MethodContextImpl.java │ │ ├── AbstractSuite.java │ │ ├── PageFactoryProxy.java │ │ ├── AbstractPage.java │ │ └── AbsractSuiteHelpers.java │ ├── resources │ └── LICENSE.txt │ └── javadoc │ └── overview.html ├── release ├── README.md └── pom.xml /.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | -------------------------------------------------------------------------------- /deploy: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | mvn clean deploy 4 | -------------------------------------------------------------------------------- /src/test/resources/testng-selenium.properties: -------------------------------------------------------------------------------- 1 | testng.selenium.logging_prefix=testing TestNG-Selenium -------------------------------------------------------------------------------- /src/main/java/com/github/jsdevel/testng/selenium/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Top level classes to use. 3 | * 4 | */ 5 | package com.github.jsdevel.testng.selenium; 6 | -------------------------------------------------------------------------------- /src/main/java/com/github/jsdevel/testng/selenium/exceptions/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Exceptions thrown by TestNG-Selenium. 3 | */ 4 | package com.github.jsdevel.testng.selenium.exceptions; 5 | -------------------------------------------------------------------------------- /src/main/java/com/github/jsdevel/testng/selenium/config/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Configuration classes used to store configuration values. 3 | */ 4 | package com.github.jsdevel.testng.selenium.config; 5 | -------------------------------------------------------------------------------- /release: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | mvn release:clean release:prepare release:perform 4 | git push origin master 5 | rm -rf /tmp/apidocs 6 | mv target/apidocs /tmp 7 | git checkout gh-pages 8 | cp -R /tmp/apidocs/* . 9 | -------------------------------------------------------------------------------- /src/main/java/com/github/jsdevel/testng/selenium/annotations/driverconfig/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Annotations used to configure the chosen WebDriver. 3 | */ 4 | package com.github.jsdevel.testng.selenium.annotations.driverconfig; 5 | -------------------------------------------------------------------------------- /src/main/java/com/github/jsdevel/testng/selenium/annotations/drivers/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Annotations used to set the WebDriver used for a particular test run. 3 | */ 4 | package com.github.jsdevel.testng.selenium.annotations.drivers; 5 | -------------------------------------------------------------------------------- /src/main/java/com/github/jsdevel/testng/selenium/annotations/screensizes/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Annotations used to set the screen size for a particular test run. 3 | */ 4 | package com.github.jsdevel.testng.selenium.annotations.screensizes; 5 | -------------------------------------------------------------------------------- /src/test/resources/testng.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/test/java/com/github/jsdevel/testng/selenium/samples/GoogleSearchResultsPage.java: -------------------------------------------------------------------------------- 1 | package com.github.jsdevel.testng.selenium.samples; 2 | 3 | import com.github.jsdevel.testng.selenium.AbstractPage; 4 | import java.net.URL; 5 | 6 | public class GoogleSearchResultsPage extends AbstractPage { 7 | @Override 8 | public boolean isPageViewableFrom(URL proposedUrl) { 9 | return "/search".equals(proposedUrl.getPath()); 10 | } 11 | } -------------------------------------------------------------------------------- /src/test/java/com/github/jsdevel/testng/selenium/samples/SamplePageFactory.java: -------------------------------------------------------------------------------- 1 | package com.github.jsdevel.testng.selenium.samples; 2 | 3 | import com.github.jsdevel.testng.selenium.PageFactory; 4 | 5 | public interface SamplePageFactory extends PageFactory { 6 | GoogleHomePage getHomePage(); 7 | GoogleHomePage getHomePage(String path); 8 | GoogleSearchResultsPage getSearchResultsPage(); 9 | GoogleSearchResultsPage getSearchResultsPage(String path); 10 | 11 | // Used to throw an Exception in the test. 12 | String getFoo(); 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/com/github/jsdevel/testng/selenium/exceptions/PageInstantiationException.java: -------------------------------------------------------------------------------- 1 | package com.github.jsdevel.testng.selenium.exceptions; 2 | 3 | /** 4 | * Thrown during {@link com.github.jsdevel.testng.selenium.Page} instantiation 5 | * if something went wrong. 6 | * 7 | * @author Joe Spencer 8 | */ 9 | public class PageInstantiationException extends RuntimeException { 10 | public PageInstantiationException(String message) { 11 | super(message); 12 | } 13 | 14 | public PageInstantiationException(Throwable cause) { 15 | super(cause); 16 | } 17 | } -------------------------------------------------------------------------------- /src/main/java/com/github/jsdevel/testng/selenium/exceptions/PageInitializationException.java: -------------------------------------------------------------------------------- 1 | package com.github.jsdevel.testng.selenium.exceptions; 2 | 3 | /** 4 | * Thrown during {@link com.github.jsdevel.testng.selenium.Page} initialization 5 | * if something went wrong. 6 | * 7 | * @author Joe Spencer 8 | */ 9 | public class PageInitializationException extends RuntimeException { 10 | public PageInitializationException(String message) { 11 | super(message); 12 | } 13 | 14 | public PageInitializationException(Throwable cause) { 15 | super(cause); 16 | } 17 | } -------------------------------------------------------------------------------- /src/main/java/com/github/jsdevel/testng/selenium/PageFactory.java: -------------------------------------------------------------------------------- 1 | package com.github.jsdevel.testng.selenium; 2 | 3 | /** 4 | * A PageFactory that is configurable by {@link AbstractSuite}s. 5 | * 6 | * Defining methods that take multiple {@link String} arguments is prohibited, 7 | * as any {@link String} argument provided will be used as the desired URL when 8 | * initializing the desired Page. 9 | * 10 | * All methods of this PageFactory MUST return sub classes of {@link Page}. 11 | * 12 | * @author Joe Spencer 13 | */ 14 | public interface PageFactory { 15 | 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/com/github/jsdevel/testng/selenium/exceptions/MissingPageFactoryException.java: -------------------------------------------------------------------------------- 1 | package com.github.jsdevel.testng.selenium.exceptions; 2 | 3 | /** 4 | * Thrown to indicate that a {@link java.lang.reflect.Method} annotated with 5 | * {@link org.testng.annotations.Test} was declared in a class that was missing 6 | * {@link com.github.jsdevel.testng.selenium.PageFactory}. 7 | * 8 | * @author Joe Spencer 9 | */ 10 | public class MissingPageFactoryException extends RuntimeException { 11 | public MissingPageFactoryException(String message) { 12 | super(message); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/com/github/jsdevel/testng/selenium/annotations/driverconfig/UserAgent.java: -------------------------------------------------------------------------------- 1 | package com.github.jsdevel.testng.selenium.annotations.driverconfig; 2 | 3 | import java.lang.annotation.Documented; 4 | import java.lang.annotation.ElementType; 5 | import java.lang.annotation.Retention; 6 | import java.lang.annotation.RetentionPolicy; 7 | import java.lang.annotation.Target; 8 | 9 | /** 10 | * This annotation allows individual tests to configure the user agent in the 11 | * current {@link org.openqa.selenium.WebDriver}. 12 | * 13 | * @author Joseph Spencer 14 | */ 15 | @Documented 16 | @Retention(RetentionPolicy.RUNTIME) 17 | @Target(ElementType.METHOD) 18 | public @interface UserAgent { 19 | String value(); 20 | } 21 | -------------------------------------------------------------------------------- /src/test/java/com/github/jsdevel/testng/selenium/samples/GoogleHomePage.java: -------------------------------------------------------------------------------- 1 | package com.github.jsdevel.testng.selenium.samples; 2 | 3 | import com.github.jsdevel.testng.selenium.AbstractPage; 4 | import java.net.URL; 5 | import org.openqa.selenium.WebElement; 6 | import org.openqa.selenium.support.FindBy; 7 | 8 | public class GoogleHomePage extends AbstractPage { 9 | @FindBy(css = "[name=q]") 10 | public WebElement q; 11 | 12 | public GoogleSearchResultsPage submitSearch(String query) { 13 | q.sendKeys(query); 14 | q.submit(); 15 | return getPageFactory().getSearchResultsPage(); 16 | } 17 | 18 | @Override 19 | protected boolean isPageViewableFrom(URL proposedUrl) { 20 | return proposedUrl.getPath().startsWith("/"); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/com/github/jsdevel/testng/selenium/annotations/drivers/Chrome.java: -------------------------------------------------------------------------------- 1 | package com.github.jsdevel.testng.selenium.annotations.drivers; 2 | 3 | import java.lang.annotation.Documented; 4 | import java.lang.annotation.ElementType; 5 | import java.lang.annotation.Retention; 6 | import java.lang.annotation.RetentionPolicy; 7 | import java.lang.annotation.Target; 8 | 9 | /** 10 | * Using this annotation with {@link org.testng.annotations.Test} will cause the 11 | * current {@link com.github.jsdevel.testng.selenium.MethodContext} to receive a 12 | * {@link org.openqa.selenium.WebDriver} instance of 13 | * {@link org.openqa.selenium.chrome.ChromeDriver}. 14 | * 15 | * @author Joseph Spencer 16 | */ 17 | @Documented 18 | @Retention(RetentionPolicy.RUNTIME) 19 | @Target(ElementType.METHOD) 20 | public @interface Chrome { 21 | 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/com/github/jsdevel/testng/selenium/annotations/drivers/Firefox.java: -------------------------------------------------------------------------------- 1 | package com.github.jsdevel.testng.selenium.annotations.drivers; 2 | 3 | import java.lang.annotation.Documented; 4 | import java.lang.annotation.ElementType; 5 | import java.lang.annotation.Retention; 6 | import java.lang.annotation.RetentionPolicy; 7 | import java.lang.annotation.Target; 8 | 9 | /** 10 | * Using this annotation with {@link org.testng.annotations.Test} will cause the 11 | * current {@link com.github.jsdevel.testng.selenium.MethodContext} to receive a 12 | * {@link org.openqa.selenium.WebDriver} instance of 13 | * {@link org.openqa.selenium.firefox.FirefoxDriver}. 14 | * 15 | * @author Joseph Spencer 16 | */ 17 | @Documented 18 | @Retention(RetentionPolicy.RUNTIME) 19 | @Target(ElementType.METHOD) 20 | public @interface Firefox { 21 | 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/com/github/jsdevel/testng/selenium/annotations/drivers/PhantomJS.java: -------------------------------------------------------------------------------- 1 | package com.github.jsdevel.testng.selenium.annotations.drivers; 2 | 3 | import java.lang.annotation.Documented; 4 | import java.lang.annotation.ElementType; 5 | import java.lang.annotation.Retention; 6 | import java.lang.annotation.RetentionPolicy; 7 | import java.lang.annotation.Target; 8 | 9 | /** 10 | * Using this annotation with {@link org.testng.annotations.Test} will cause the 11 | * current {@link com.github.jsdevel.testng.selenium.MethodContext} to receive a 12 | * {@link org.openqa.selenium.WebDriver} instance of 13 | * {@link org.openqa.selenium.phantomjs.PhantomJSDriver}. 14 | * 15 | * @author Joseph Spencer 16 | */ 17 | @Documented 18 | @Retention(RetentionPolicy.RUNTIME) 19 | @Target(ElementType.METHOD) 20 | public @interface PhantomJS { 21 | 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/com/github/jsdevel/testng/selenium/annotations/drivers/InternetExplorer.java: -------------------------------------------------------------------------------- 1 | package com.github.jsdevel.testng.selenium.annotations.drivers; 2 | 3 | import java.lang.annotation.Documented; 4 | import java.lang.annotation.ElementType; 5 | import java.lang.annotation.Retention; 6 | import java.lang.annotation.RetentionPolicy; 7 | import java.lang.annotation.Target; 8 | 9 | /** 10 | * Using this annotation with {@link org.testng.annotations.Test} will cause the 11 | * current {@link com.github.jsdevel.testng.selenium.MethodContext} to receive a 12 | * {@link org.openqa.selenium.WebDriver} instance of 13 | * {@link org.openqa.selenium.ie.InternetExplorerDriver}. 14 | * 15 | * @author Joseph Spencer 16 | */ 17 | @Documented 18 | @Retention(RetentionPolicy.RUNTIME) 19 | @Target(ElementType.METHOD) 20 | public @interface InternetExplorer { 21 | 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/com/github/jsdevel/testng/selenium/annotations/screensizes/Phone.java: -------------------------------------------------------------------------------- 1 | package com.github.jsdevel.testng.selenium.annotations.screensizes; 2 | 3 | import java.lang.annotation.Documented; 4 | import java.lang.annotation.ElementType; 5 | import java.lang.annotation.Retention; 6 | import java.lang.annotation.RetentionPolicy; 7 | import java.lang.annotation.Target; 8 | 9 | /** 10 | * Using this annotation with {@link org.testng.annotations.Test} will cause the 11 | * current {@link com.github.jsdevel.testng.selenium.MethodContext} to receive a 12 | * {@link org.openqa.selenium.WebDriver} instance set to Phone 13 | * dimension 426x320 (based off of bootstrap breakpoints). 14 | * 15 | * @author Joseph Spencer 16 | */ 17 | @Documented 18 | @Retention(RetentionPolicy.RUNTIME) 19 | @Target(ElementType.METHOD) 20 | public @interface Phone { 21 | int height() default 320; 22 | int width() default 426; 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/com/github/jsdevel/testng/selenium/annotations/screensizes/Tablet.java: -------------------------------------------------------------------------------- 1 | package com.github.jsdevel.testng.selenium.annotations.screensizes; 2 | 3 | import java.lang.annotation.Documented; 4 | import java.lang.annotation.ElementType; 5 | import java.lang.annotation.Retention; 6 | import java.lang.annotation.RetentionPolicy; 7 | import java.lang.annotation.Target; 8 | 9 | /** 10 | * Using this annotation with {@link org.testng.annotations.Test} will cause the 11 | * current {@link com.github.jsdevel.testng.selenium.MethodContext} to receive a 12 | * {@link org.openqa.selenium.WebDriver} instance set to Tablet 13 | * dimension 768x500 (based off of bootstrap breakpoints). 14 | * 15 | * @author Joseph Spencer 16 | */ 17 | @Documented 18 | @Retention(RetentionPolicy.RUNTIME) 19 | @Target(ElementType.METHOD) 20 | public @interface Tablet { 21 | int height() default 500; 22 | int width() default 768; 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/com/github/jsdevel/testng/selenium/annotations/screensizes/Desktop.java: -------------------------------------------------------------------------------- 1 | package com.github.jsdevel.testng.selenium.annotations.screensizes; 2 | 3 | import java.lang.annotation.Documented; 4 | import java.lang.annotation.ElementType; 5 | import java.lang.annotation.Retention; 6 | import java.lang.annotation.RetentionPolicy; 7 | import java.lang.annotation.Target; 8 | 9 | /** 10 | * Using this annotation with {@link org.testng.annotations.Test} will cause the 11 | * current {@link com.github.jsdevel.testng.selenium.MethodContext} to receive a 12 | * {@link org.openqa.selenium.WebDriver} instance set to Desktop 13 | * dimension 992x600 (based off of bootstrap breakpoints). 14 | * 15 | * @author Joseph Spencer 16 | */ 17 | @Documented 18 | @Retention(RetentionPolicy.RUNTIME) 19 | @Target(ElementType.METHOD) 20 | public @interface Desktop { 21 | int height() default 600; 22 | int width() default 992; 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/com/github/jsdevel/testng/selenium/annotations/screensizes/LargeDesktop.java: -------------------------------------------------------------------------------- 1 | package com.github.jsdevel.testng.selenium.annotations.screensizes; 2 | 3 | import java.lang.annotation.Documented; 4 | import java.lang.annotation.ElementType; 5 | import java.lang.annotation.Retention; 6 | import java.lang.annotation.RetentionPolicy; 7 | import java.lang.annotation.Target; 8 | 9 | /** 10 | * Using this annotation with {@link org.testng.annotations.Test} will cause the 11 | * current {@link com.github.jsdevel.testng.selenium.MethodContext} to receive a 12 | * {@link org.openqa.selenium.WebDriver} instance set to a Large Desktop 13 | * dimension 1200x800 (based off of bootstrap breakpoints). 14 | * 15 | * @author Joseph Spencer 16 | */ 17 | @Documented 18 | @Retention(RetentionPolicy.RUNTIME) 19 | @Target(ElementType.METHOD) 20 | public @interface LargeDesktop { 21 | int height() default 800; 22 | int width() default 1200; 23 | } 24 | -------------------------------------------------------------------------------- /src/main/resources/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015 Joseph Spencer 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /src/main/java/com/github/jsdevel/testng/selenium/config/ConfigDefaults.java: -------------------------------------------------------------------------------- 1 | package com.github.jsdevel.testng.selenium.config; 2 | 3 | /** 4 | * Constants representing default configuration values. 5 | * 6 | * @see Config 7 | * 8 | * @author Joe Spencer 9 | */ 10 | class ConfigDefaults { 11 | 12 | /** 13 | * String value of "false". 14 | */ 15 | static final String DEBUG = "disabled"; 16 | 17 | /** 18 | * String value of "PhantomJS". 19 | */ 20 | static final String DRIVER = "PhantomJS"; 21 | 22 | /** 23 | * No default endpoint is provided. 24 | */ 25 | static final String ENDPOINT = null; 26 | 27 | /** 28 | * String value of "TestNG-Selenium". 29 | */ 30 | static final String LOGGING_PREFIX = "TestNG-Selenium"; 31 | 32 | /** 33 | * String value of "Phone". 34 | */ 35 | static final String SCREENSIZE = "Phone"; 36 | 37 | /** 38 | * Value of System.getProperty("java.io.tmpdir"). 39 | */ 40 | static final String TMPDIR = System.getProperty("java.io.tmpdir"); 41 | 42 | /** 43 | * No default user agent is provided. 44 | */ 45 | static final String USER_AGENT = null; 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/com/github/jsdevel/testng/selenium/config/ConfigOptions.java: -------------------------------------------------------------------------------- 1 | package com.github.jsdevel.testng.selenium.config; 2 | 3 | /** 4 | * Configuration options representing system and property file key names 5 | * recognized by testng-selenium. 6 | * 7 | * @see Config 8 | * 9 | * @author Joe Spencer 10 | */ 11 | public class ConfigOptions { 12 | /** 13 | * String value of "testng.selenium.debug". 14 | */ 15 | public static final String DEBUG = "testng.selenium.debug"; 16 | 17 | /** 18 | * String value of "testng.selenium.driver". 19 | */ 20 | public static final String DRIVER = "testng.selenium.driver"; 21 | 22 | /** 23 | * String value of "testng.selenium.endpoint". 24 | */ 25 | public static final String ENDPOINT = "testng.selenium.endpoint"; 26 | 27 | /** 28 | * String value of "testng.selenium.logging_prefix". 29 | */ 30 | public static final String LOGGING_PREFIX = "testng.selenium.logging_prefix"; 31 | 32 | /** 33 | * String value of "testng.selenium.screensize". 34 | */ 35 | public static final String SCREENSIZE = "testng.selenium.screensize"; 36 | 37 | /** 38 | * String value of "testng.selenium.tmpdir". 39 | */ 40 | public static final String TMPDIR = "testng.selenium.tmpdir"; 41 | 42 | /** 43 | * String value of "testng.selenium.driverconfig.useragent". 44 | */ 45 | public static final String USER_AGENT = "testng.selenium.driverconfig.useragent"; 46 | 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/com/github/jsdevel/testng/selenium/Page.java: -------------------------------------------------------------------------------- 1 | package com.github.jsdevel.testng.selenium; 2 | 3 | import java.net.URL; 4 | import org.openqa.selenium.WebDriver; 5 | 6 | /** 7 | * Represents a page that has been configured. 8 | * 9 | * @param

The configured Page. 10 | * @param The PageFactory that build this page. 11 | * @see AbstractPage 12 | * @author Joe Spencer 13 | */ 14 | public interface Page

{ 15 | /** 16 | * Returns the context of the Method annotated with 17 | * {@link org.testng.annotations.Test}. 18 | * 19 | * @return The method context. 20 | */ 21 | MethodContext getContext(); 22 | 23 | /** 24 | * Returns the endpoint that was configured for the current test run. 25 | * 26 | * @return The endpoint. 27 | */ 28 | String getEndpoint(); 29 | 30 | /** 31 | * Returns the URL that was used during page initialization. 32 | * 33 | * @return The initial URL. 34 | */ 35 | URL getInitialUrl(); 36 | 37 | /** 38 | * Returns this Page. 39 | * 40 | * @return This Page. 41 | */ 42 | Page getPage(); 43 | 44 | /** 45 | * The PageFactory instance that built this Page. 46 | * 47 | * @return The PageFactory that built this Page. 48 | */ 49 | PF getPageFactory(); 50 | 51 | /** 52 | * Returns the {@link org.openqa.selenium.WebDriver} that has been configured 53 | * for this test run. 54 | * 55 | * @return The {@link org.openqa.selenium.WebDriver}. 56 | */ 57 | WebDriver getWebDriver(); 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/com/github/jsdevel/testng/selenium/TestNGSeleniumLogger.java: -------------------------------------------------------------------------------- 1 | package com.github.jsdevel.testng.selenium; 2 | 3 | import com.github.jsdevel.testng.selenium.config.Config; 4 | import java.util.List; 5 | 6 | /** 7 | * A logger that prefixes output. 8 | * 9 | * @see com.github.jsdevel.testng.selenium.config.Config 10 | * 11 | * @author Joe Spencer 12 | */ 13 | public class TestNGSeleniumLogger { 14 | /** 15 | * Logs output with a logging prefix (debug mode enabled only). 16 | * 17 | * @see com.github.jsdevel.testng.selenium.config.Config 18 | * 19 | * @param msg The message to log. 20 | */ 21 | public static void debug(String msg) { 22 | if (Config.DEBUG) { 23 | log(msg); 24 | } 25 | } 26 | 27 | /** 28 | * Logs output with a logging prefix. 29 | * 30 | * @see com.github.jsdevel.testng.selenium.config.Config 31 | * 32 | * @param msg The message to log. 33 | */ 34 | public static void log(String msg) { 35 | System.out.println(prefixed(msg)); 36 | } 37 | 38 | /** 39 | * Logs a group of messages in a single buffered operation. Using this method 40 | * will not interleave output with other tests when running in parallel. 41 | * 42 | * @param messages The messages to log. 43 | */ 44 | public static void log(List messages) { 45 | if (messages != null) { 46 | StringBuilder builder = new StringBuilder(); 47 | for (String msg: messages) { 48 | builder.append(prefixed(msg)).append('\n'); 49 | } 50 | System.out.print(builder.toString()); 51 | } 52 | } 53 | 54 | private static String prefixed(String msg) { 55 | return "[" + Config.LOGGING_PREFIX + "] " + msg; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/com/github/jsdevel/testng/selenium/MethodContext.java: -------------------------------------------------------------------------------- 1 | package com.github.jsdevel.testng.selenium; 2 | 3 | import org.openqa.selenium.WebDriver; 4 | 5 | /** 6 | * A context created for each test run. 7 | * 8 | * @author Joe Spencer 9 | * @param The {@link PageFactory} configured by {@link AbstractSuite}. 10 | */ 11 | public interface MethodContext { 12 | /** 13 | * Returns the configured endpoint for this test run. 14 | * 15 | * @see com.github.jsdevel.testng.selenium.config.Config#ENDPOINT 16 | * @return The configured endpoint. 17 | */ 18 | String getEndpoint(); 19 | 20 | /** 21 | * Returns the configured {@link PageFactory} for this test run. 22 | * 23 | * @return The configured {@link PageFactory}. 24 | */ 25 | PF getPageFactory(); 26 | 27 | /** 28 | * Returns the screen size that has been configured for this test run. 29 | * 30 | * @return The configured screen size. 31 | */ 32 | Object getScreensize(); 33 | 34 | /** 35 | * Returns the configured {@link WebDriver} for this test run. 36 | * @see com.github.jsdevel.testng.selenium.config.Config#DRIVER 37 | * @see com.github.jsdevel.testng.selenium.annotations.drivers 38 | * 39 | * @return The configured driver; 40 | */ 41 | WebDriver getWebDriver(); 42 | 43 | /** 44 | * Returns the configured 45 | * {@link com.github.jsdevel.testng.selenium.annotations.driverconfig.UserAgent} 46 | * for this test run. 47 | * 48 | * @see com.github.jsdevel.testng.selenium.config.Config#USER_AGENT 49 | * @see com.github.jsdevel.testng.selenium.annotations.driverconfig.UserAgent 50 | * 51 | * @return The configured UserAgent String. 52 | */ 53 | String getUserAgent(); 54 | 55 | /** 56 | * Logs a message for future processing. 57 | * 58 | * @param msg The message to log. 59 | */ 60 | void log(String msg); 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/com/github/jsdevel/testng/selenium/MethodContextImpl.java: -------------------------------------------------------------------------------- 1 | package com.github.jsdevel.testng.selenium; 2 | 3 | import java.lang.reflect.Method; 4 | import java.util.ArrayList; 5 | import java.util.Collections; 6 | import java.util.List; 7 | import org.openqa.selenium.WebDriver; 8 | 9 | 10 | class MethodContextImpl implements MethodContext { 11 | final Method method; 12 | private PF pageFactory; 13 | private WebDriver webDriver; 14 | private final List output; 15 | private Object screensize; 16 | private String userAgent; 17 | private String endpoint; 18 | 19 | /** 20 | * Constructs this context with the {@link Method} representing the current 21 | * TestNG test run. 22 | * 23 | * @param method The current test method being run. 24 | */ 25 | public MethodContextImpl(Method method) { 26 | this.method = method; 27 | this.output = new ArrayList<>(); 28 | } 29 | 30 | @Override 31 | public String getEndpoint() { 32 | return endpoint; 33 | } 34 | 35 | @Override 36 | public PF getPageFactory() { 37 | return this.pageFactory; 38 | } 39 | 40 | @Override 41 | public Object getScreensize() { 42 | return this.screensize; 43 | } 44 | 45 | @Override 46 | public WebDriver getWebDriver() { 47 | return this.webDriver; 48 | } 49 | 50 | @Override 51 | public String getUserAgent() { 52 | return this.userAgent; 53 | } 54 | 55 | @Override 56 | public void log(String msg) { 57 | this.output.add(msg); 58 | } 59 | 60 | List getOutput() { 61 | return Collections.unmodifiableList(output); 62 | } 63 | 64 | void setEndpoint(String endpoint) { 65 | this.endpoint = endpoint; 66 | } 67 | void setPageFactory(PF pageFactory) { 68 | this.pageFactory = pageFactory; 69 | } 70 | 71 | void setScreensize(Object screensize) { 72 | this.screensize = screensize; 73 | } 74 | 75 | void setWebDriver(WebDriver webDriver) { 76 | this.webDriver = webDriver; 77 | } 78 | 79 | void setUserAgent(String userAgent) { 80 | this.userAgent = userAgent; 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/main/java/com/github/jsdevel/testng/selenium/AbstractSuite.java: -------------------------------------------------------------------------------- 1 | package com.github.jsdevel.testng.selenium; 2 | 3 | import com.github.jsdevel.testng.selenium.config.Config; 4 | import java.io.IOException; 5 | import java.lang.reflect.Method; 6 | import org.testng.ITestResult; 7 | import org.testng.annotations.AfterMethod; 8 | import org.testng.annotations.BeforeMethod; 9 | 10 | /** 11 | * An AbstractSuite that is thread safe, allows methods to be run in 12 | * parallel, and creates instances of interfaces that extend 13 | * {@link com.github.jsdevel.testng.selenium.PageFactory}. This suite also 14 | * manages {@link org.openqa.selenium.WebDriver} instances, 15 | * by creating and configuring them before a test, and destroying them after a 16 | * test. 17 | * 18 | * @author Joe Spencer 19 | * @param The {@link com.github.jsdevel.testng.selenium.PageFactory} 20 | * interface used to instantiate 21 | * {@link com.github.jsdevel.testng.selenium.Page}s during tests. 22 | */ 23 | public class AbstractSuite { 24 | private final ThreadLocal> methodContext = new ThreadLocal<>(); 25 | 26 | /** 27 | * Returns an implementation of the 28 | * {@link com.github.jsdevel.testng.selenium.PageFactory} passed as a Type 29 | * parameter to this Suite. All methods of the factory should return 30 | * instances of {@link com.github.jsdevel.testng.selenium.Page}. 31 | * 32 | * @return An implementation of {@link PageFactory}. 33 | */ 34 | protected PF getPageFactory() { 35 | return methodContext.get().getPageFactory(); 36 | } 37 | 38 | @BeforeMethod(alwaysRun = true) 39 | public void beforeMethod(Method method) { 40 | MethodContextImpl context = new MethodContextImpl<>(method); 41 | 42 | context.setEndpoint(Config.ENDPOINT); 43 | AbsractSuiteHelpers.addUserAgent(context); 44 | AbsractSuiteHelpers.addWebDriver(context); 45 | AbsractSuiteHelpers.addScreensize(context); 46 | AbsractSuiteHelpers.addPageFactory(context); 47 | context.log("Starting test method " + AbsractSuiteHelpers.getTestName( 48 | method)); 49 | 50 | methodContext.set(context); 51 | } 52 | 53 | @AfterMethod(alwaysRun = true) 54 | public void afterMethod(ITestResult testResult) throws IOException { 55 | MethodContextImpl context = methodContext.get(); 56 | 57 | if (context == null) { 58 | return; 59 | } 60 | 61 | if (testResult.getStatus() == ITestResult.FAILURE) { 62 | AbsractSuiteHelpers.takeScreenshot(context); 63 | 64 | TestNGSeleniumLogger.log(context.getOutput()); 65 | } 66 | 67 | context.getWebDriver().quit(); 68 | 69 | methodContext.remove(); 70 | } 71 | } -------------------------------------------------------------------------------- /src/test/java/com/github/jsdevel/testng/selenium/samples/SampleSuiteITest.java: -------------------------------------------------------------------------------- 1 | package com.github.jsdevel.testng.selenium.samples; 2 | 3 | import com.github.jsdevel.testng.selenium.AbstractSuite; 4 | import com.github.jsdevel.testng.selenium.annotations.driverconfig.UserAgent; 5 | import com.github.jsdevel.testng.selenium.annotations.screensizes.LargeDesktop; 6 | import com.github.jsdevel.testng.selenium.annotations.screensizes.Phone; 7 | import com.github.jsdevel.testng.selenium.config.Config; 8 | import com.github.jsdevel.testng.selenium.exceptions.PageInitializationException; 9 | import com.github.jsdevel.testng.selenium.exceptions.PageInstantiationException; 10 | import static org.testng.AssertJUnit.assertEquals; 11 | import static org.testng.AssertJUnit.assertNotNull; 12 | import org.testng.annotations.Test; 13 | 14 | public class SampleSuiteITest extends AbstractSuite { 15 | @Test 16 | public void we_should_be_able_to_view_google_with_phantom() { 17 | getPageFactory().getHomePage(); 18 | } 19 | 20 | @Test 21 | public void we_should_be_able_to_pass_desired_urls_to_pages() { 22 | getPageFactory() 23 | .getHomePage("/intl/en/policies/privacy/?fg=1") 24 | ; 25 | } 26 | 27 | @Test 28 | public void we_should_be_able_to_return_new_pages_from_pages() { 29 | GoogleSearchResultsPage searchPage = getPageFactory() 30 | .getHomePage() 31 | .submitSearch("dinosaurs") 32 | ; 33 | } 34 | 35 | @Test(expectedExceptions = PageInstantiationException.class) 36 | public void we_should_get_an_error_when_calling_factory_methods_with_a_return_type_that_does_not_extend_Page() { 37 | getPageFactory().getFoo(); 38 | } 39 | 40 | @Test(expectedExceptions = PageInitializationException.class, expectedExceptionsMessageRegExp = ".+can not be viewed from.+") 41 | public void we_should_get_an_error_when_initializing_a_page_that_is_not_viewable_from_the_current_url() { 42 | getPageFactory().getSearchResultsPage("/asdfasdf"); 43 | } 44 | 45 | @Test @LargeDesktop 46 | public void we_should_be_able_to_control_screensize_via_annotations() { 47 | LargeDesktop screensize = (LargeDesktop) getPageFactory().getHomePage() 48 | .getContext().getScreensize(); 49 | assertNotNull(screensize); 50 | } 51 | 52 | @Test 53 | public void we_should_be_default_screensize_to_Phone() { 54 | Phone screensize = (Phone) getPageFactory().getHomePage() 55 | .getContext().getScreensize(); 56 | assertNotNull(screensize); 57 | } 58 | 59 | @Test @UserAgent("foo") 60 | public void we_should_be_able_to_set_the_user_agent_on_a_test_by_test_basis() { 61 | getPageFactory(); 62 | } 63 | 64 | @Test 65 | public void we_should_be_able_to_configure_values_from_properties_files() { 66 | assertEquals("testing TestNG-Selenium", Config.LOGGING_PREFIX); 67 | } 68 | } -------------------------------------------------------------------------------- /src/main/java/com/github/jsdevel/testng/selenium/PageFactoryProxy.java: -------------------------------------------------------------------------------- 1 | package com.github.jsdevel.testng.selenium; 2 | 3 | import com.github.jsdevel.testng.selenium.exceptions.PageInstantiationException; 4 | import java.lang.reflect.InvocationHandler; 5 | import java.lang.reflect.Method; 6 | import java.lang.reflect.Proxy; 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | 10 | /** 11 | * A Proxy implementation of an interface that extends PageFactory. All methods 12 | of the sub interface must have a return type that directly extends 13 | AbstractPage. All pages returned from this PageFactory will be initialized. 14 | * 15 | * @author Joe Spencer 16 | * @param The PageFactory this Proxy wraps. 17 | */ 18 | class PageFactoryProxy implements InvocationHandler { 19 | private final MethodContext context; 20 | private final Class pageFactoryClass; 21 | 22 | PageFactoryProxy(Class pageFactoryClass, MethodContext context) { 23 | this.context = context; 24 | this.pageFactoryClass = pageFactoryClass; 25 | } 26 | 27 | @SuppressWarnings("unchecked") 28 | static PF newInstance(Class pageFactoryClass, MethodContext context) { 29 | return (PF) Proxy.newProxyInstance(pageFactoryClass.getClassLoader(), 30 | new Class[] {pageFactoryClass}, new PageFactoryProxy(pageFactoryClass, 31 | context)); 32 | } 33 | 34 | @Override 35 | @SuppressWarnings("unchecked") 36 | public Object invoke(Object proxy, Method pageFactoryMethod, Object[] args) throws Throwable { 37 | Object page = pageFactoryMethod.getReturnType().newInstance(); 38 | 39 | if (page instanceof AbstractPage) { 40 | AbstractPage abstractPage = (AbstractPage) page; 41 | abstractPage.initialize(getDesiredUrl(pageFactoryMethod, args), context); 42 | } else { 43 | throw new PageInstantiationException("Pages returned from " + 44 | pageFactoryClass.getName() + " must return instances of " + 45 | AbstractPage.class.getName()); 46 | } 47 | 48 | return page; 49 | } 50 | 51 | /** 52 | * Return the desired URL from the arguments passed to the PageFactory method. 53 | * The first String argument found is inferred to be the desired URL. If 54 | * multiple String arguments given, then an Exception is thrown with the 55 | * location of the method in the message. 56 | * 57 | * @param method Used then throwing an exception to inform the user where 58 | * multiple String arguments were given. 59 | * @param args 60 | * @throws PageInstantiationException If multiple String arguments were given. 61 | * @return the desired URL. 62 | */ 63 | private String getDesiredUrl(Method method, Object[] args) { 64 | if (args == null || args.length == 0) { 65 | return ""; 66 | } 67 | 68 | List stringArgs = new ArrayList<>(); 69 | for (Object arg: args) { 70 | if (arg instanceof String) { 71 | stringArgs.add((String) arg); 72 | } 73 | } 74 | 75 | switch (stringArgs.size()) { 76 | case 0: 77 | return ""; 78 | case 1: 79 | return stringArgs.get(0) == null ? "" : stringArgs.get(0); 80 | default: 81 | throw new PageInstantiationException( 82 | "Multiple String arguments are not allowed in " + method.getName()); 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/main/java/com/github/jsdevel/testng/selenium/config/PropertiesConfig.java: -------------------------------------------------------------------------------- 1 | package com.github.jsdevel.testng.selenium.config; 2 | 3 | import java.io.IOException; 4 | import java.io.InputStream; 5 | import java.util.Properties; 6 | import java.util.logging.Level; 7 | import java.util.logging.Logger; 8 | 9 | /** 10 | * Constants representing "classpath:/testng-selenium.properties" configuration. 11 | * 12 | * @see Config 13 | * 14 | * @author Joe Spencer 15 | */ 16 | class PropertiesConfig { 17 | /** 18 | * Taken from testng.selenium.debug in the properties file. 19 | */ 20 | static final String DEBUG; 21 | 22 | /** 23 | * Taken from testng.selenium.driver in the properties file. 24 | */ 25 | static final String DRIVER; 26 | 27 | /** 28 | * Taken from testng.selenium.endpoint in the properties file. 29 | */ 30 | static final String ENDPOINT; 31 | 32 | /** 33 | * Taken from testng.selenium.logging_prefix in the properties file. 34 | */ 35 | static final String LOGGING_PREFIX; 36 | 37 | /** 38 | * Taken from testng.selenium.screensize in the properties file. 39 | */ 40 | static final String SCREENSIZE; 41 | 42 | /** 43 | * Taken from testng.selenium.tmpdir in the properties file. 44 | */ 45 | static final String TMPDIR; 46 | 47 | /** 48 | * Taken from testng.selenium.driverconfig.useragent in the properties file. 49 | */ 50 | static final String USER_AGENT; 51 | 52 | static { 53 | String debug = ConfigDefaults.DEBUG; 54 | String driver = ConfigDefaults.DRIVER; 55 | String endpoint = ConfigDefaults.ENDPOINT; 56 | String loggingPrefix = ConfigDefaults.LOGGING_PREFIX; 57 | String screensize = ConfigDefaults.SCREENSIZE; 58 | String tmpDir = ConfigDefaults.TMPDIR; 59 | String userAgent = ConfigDefaults.USER_AGENT; 60 | 61 | try (InputStream propertiesInputStream = PropertiesConfig.class 62 | .getResourceAsStream("/testng-selenium.properties")) { 63 | if (propertiesInputStream != null) { 64 | try { 65 | Properties properties = new Properties(); 66 | properties.load(propertiesInputStream); 67 | debug = properties.getProperty(ConfigOptions.DEBUG, debug); 68 | driver = properties.getProperty(ConfigOptions.DRIVER, driver); 69 | endpoint = properties.getProperty(ConfigOptions.ENDPOINT, endpoint); 70 | loggingPrefix = properties.getProperty( 71 | ConfigOptions.LOGGING_PREFIX, loggingPrefix); 72 | screensize = properties.getProperty( 73 | ConfigOptions.SCREENSIZE, screensize); 74 | tmpDir = properties.getProperty(ConfigOptions.TMPDIR, tmpDir); 75 | userAgent = properties.getProperty( 76 | ConfigOptions.USER_AGENT, userAgent); 77 | } catch (IOException e) { 78 | Logger.getLogger(PropertiesConfig.class.getName()) 79 | .log(Level.SEVERE, "Failed to load testng-selenium.properties.", e); 80 | System.exit(1); 81 | } 82 | } 83 | } catch (IOException e) { 84 | Logger.getLogger(PropertiesConfig.class.getName()) 85 | .log(Level.SEVERE, "Failed to close testng-selenium.properties.", e); 86 | System.exit(1); 87 | } 88 | 89 | DEBUG = debug; 90 | DRIVER = driver; 91 | ENDPOINT = endpoint; 92 | LOGGING_PREFIX = loggingPrefix; 93 | SCREENSIZE = screensize; 94 | TMPDIR = tmpDir; 95 | USER_AGENT = userAgent; 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/main/java/com/github/jsdevel/testng/selenium/AbstractPage.java: -------------------------------------------------------------------------------- 1 | package com.github.jsdevel.testng.selenium; 2 | 3 | import com.github.jsdevel.testng.selenium.exceptions.PageInitializationException; 4 | import java.net.MalformedURLException; 5 | import java.net.URL; 6 | import org.openqa.selenium.WebDriver; 7 | 8 | /** 9 | * An implementation of {@link Page} that is configured and initialized by 10 | * {@link AbstractSuite}. 11 | * 12 | * 13 | * @param

The AbstractPage that extends this. 14 | * @param The PageFactory that build this Page. 15 | * @see Page 16 | * @author Joe Spencer 17 | * 18 | */ 19 | public abstract class AbstractPage

implements Page { 21 | private MethodContext context; 22 | private String endpoint; 23 | private URL initialUrl; 24 | private PF pageFactory; 25 | private WebDriver webDriver; 26 | 27 | @Override 28 | public final MethodContext getContext() { 29 | return context; 30 | } 31 | 32 | @Override 33 | public final String getEndpoint() { 34 | return this.endpoint; 35 | } 36 | 37 | @Override 38 | public final URL getInitialUrl() { 39 | return initialUrl; 40 | } 41 | 42 | @Override 43 | public final Page getPage() { 44 | return this; 45 | } 46 | 47 | @Override 48 | public final PF getPageFactory() { 49 | return pageFactory; 50 | } 51 | 52 | @Override 53 | public final WebDriver getWebDriver() { 54 | return webDriver; 55 | } 56 | 57 | /** 58 | * Allows the page to signal whether or not it can be viewed from the provided 59 | * URL. This method is called during the initialization phase. Override as 60 | * needed for page specific validation. 61 | * 62 | * @param proposedUrl The proposed URL. 63 | * @return whether or not this page can be viewed by the proposedUrl. 64 | */ 65 | protected boolean isPageViewableFrom(URL proposedUrl) { 66 | return true; 67 | } 68 | 69 | /** 70 | * Called when this page is initialized. An initialized page may not be fully 71 | * loaded in the {@link org.openqa.selenium.WebDriver}. You may override this 72 | * method to perform actions like waiting for requests to finish, or elements 73 | * to be visible etc. 74 | */ 75 | protected void handlePageInitialized() { 76 | // do nothing by default 77 | } 78 | 79 | /** 80 | * Initializes this Page. This is an internal operation, not meant to be 81 | * exposed outside of testng-selenium. 82 | * The initialization process is as follows: 83 | * 84 | *

98 | * 99 | * @param desiredUrl 100 | * @param context 101 | * 102 | * @throws PageInitializationException If at any point during page 103 | * initialization the URL in the WebDriver is illegal or isn't viewable by 104 | * calling isPageViewableFrom. 105 | */ 106 | void initialize(String desiredUrl, MethodContext context) { 107 | this.context = context; 108 | this.endpoint = context.getEndpoint(); 109 | this.pageFactory = context.getPageFactory(); 110 | this.webDriver = context.getWebDriver(); 111 | 112 | this.initialUrl = getUrl(ofAbsoluteUrl(desiredUrl, endpoint)); 113 | 114 | context.log("Initializing " + this.getClass().getSimpleName()); 115 | 116 | if (this.webDriver.getCurrentUrl().equals("about:blank")) { 117 | context.log("Detected about:blank."); 118 | context.log("Navigating to " + this.initialUrl); 119 | this.webDriver.get(initialUrl.toString()); 120 | } 121 | 122 | context.log("Instantiating any WebElement or List fields " + 123 | "with org.openqa.selenium.support.PageFactory"); 124 | org.openqa.selenium.support.PageFactory.initElements(context.getWebDriver(), 125 | this); 126 | 127 | context.log("Page initialized. Calling handlePageInitialized()."); 128 | this.handlePageInitialized(); 129 | 130 | this.initialUrl = getUrl(webDriver.getCurrentUrl()); 131 | 132 | if (!this.isPageViewableFrom(this.initialUrl)) { 133 | context.log(this.getClass().getName() + " was not viewable from " + 134 | this.initialUrl.toString() + ". Throwing an exception."); 135 | throw new PageInitializationException(this.getClass().getName() + 136 | " can not be viewed from " + this.initialUrl.toString()); 137 | } 138 | } 139 | 140 | private String ofAbsoluteUrl(String proposedUrl, String defaultProtocolHost) { 141 | if (proposedUrl == null) { 142 | return defaultProtocolHost; 143 | } 144 | 145 | if (proposedUrl.matches("^[a-zA-Z]+://.+")) { 146 | return proposedUrl; 147 | } 148 | 149 | return defaultProtocolHost + "/" + proposedUrl.replaceFirst("^/+", ""); 150 | } 151 | 152 | private URL getUrl(String url) { 153 | try { 154 | return new URL(url); 155 | } catch (MalformedURLException e) { 156 | throw new PageInitializationException(e); 157 | } 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | testng-selenium 2 | ====================== 3 | > Remove the TestNG Selenium boilerplate. 4 | 5 |

Overview

6 |
    7 |
  1. Overview
  2. 8 |
  3. Project Goals
  4. 9 |
  5. Maven Dependency
  6. 10 |
  7. Writing Suites
  8. 11 |
  9. Writing a Page Factory
  10. 12 |
  11. Writing a Page Object
  12. 13 |
  13. Configuring 14 |
  14. 19 |
20 |

Project Goals

21 |
    22 |
  • Configuration through Property Files, System properties and annotations.
  • 23 |
  • Facilitate running tests in parallel.
  • 24 |
  • Remove typical boilerplate, such as taking screenshots on test 25 | failures, configuring WebDriver, and instantiating page objects.
  • 26 |
  • Declarative way of defining page objects with PageFactory.
  • 27 |
  • Declarative way of configuring tests individually.
  • 28 |
29 |

Maven Dependency

30 | You can add TestNG-Selenium to your existing project as follows: 31 |
32 |
33 | 34 |
35 | For new projects, you can quickly get setup by using the following command at a terminal: 36 |
37 | ```shell 38 | mvn archetype:generate \ 39 | -DarchetypeGroupId=com.github.jsdevel \ 40 | -DarchetypeArtifactId=testng-selenium-archetype 41 | ``` 42 |

Writing Suites

43 | In each of your suite classes, extend AbstractSuite and pass 44 | your PageFactory as a generic type argument. 45 |
46 |
47 | 48 |
49 | AbstractSuite provides a getPageFactory 50 | method to create the page factories passed in as generic type arguments. In 51 | this example, the page factory passed as a generic type on line 7 is 52 | returned by getPageFactory. 53 |

Writing a Page Factory

54 | A PageFactory is nothing more than an interface: 55 |
56 |
57 | 58 |
59 | Each method declared in a PageFactory should return a sub class 60 | of AbstractPage. This allows TestNG-Selenium to do some cool things 61 | when initializing your page objects, like navigating to them when 62 | WebDriver has been created, wiring up annotated fields using 63 | Selenium's PageFactory initializer, and validating that the URL currently 64 | being viewed by WebDriver is valid for the requested page. This approach 65 | also allows you to avoid boilerplate by letting TestNG-Selenium manage your 66 | page factory's lifecycle. 67 |

Writing a Page object.

68 |
    69 |
  1. Create a class I.E. GoogleHomePage
  2. 70 |
  3. Extend com.github.jsdevel.testng.selenium.AbstractPage.
  4. 71 |
  5. Pass your page object's type, and the page factory used to create it 72 | as generic type arguments to AbstractPage.
  6. 73 |
  7. Optionally add WebElement fields annotated with @FindBy (see 74 | Google's 75 | PageFactory pattern.).
  8. 76 |
77 | 78 |
79 | If you need to do something before validation occurs, such as wait for 80 | requests, or poll a global javascript variable, you can override 81 | AbstractPage#handlePageInitialized(). 82 |

Configuring

83 | TestNG-Selenium may be configured in one of 3 ways: 84 |
    85 |
  • Properties File
  • 86 |
  • System Properties
  • 87 |
  • Annotations
  • 88 |
89 | In general, annotation based configuration overrides system based 90 | configuration on a per test basis. 91 |

Property File Based Configuration

92 | TestNG-Selenium will look for a file called "testng-selenium.properties" at 93 | the root of your classpath. If found, then values contained therein will 94 | override the default configuration values. The key value pairs are the same 95 | as they are for System Based Configuration. 96 |

System Based Configuration

97 | System based configuration is driven by System properties. System properties 98 | override both properties file and default configuration. 99 |
100 | Here is a list of the system properties recognized by TestNG-Selenium with their default values: 101 |
102 |
103 | 104 |

Annotation Based Configuration

105 | Annotation based configuration can override the default configuration, 106 | properties file annotation, and system based configuration for a single test 107 | run. 108 |
109 | Here is an example of how we can override a system property using an annotation 110 | for a single test run. For the full list of supported annotations, see 111 | package contents under com.github.jsdevel.annotations. 112 |
113 |
114 | 115 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | TestNG Selenium 5 | A library for easy TestNG Selenium testing. 6 | com.github.jsdevel 7 | testng-selenium 8 | 0.11.3-SNAPSHOT 9 | jar 10 | https://github.com/jsdevel/java-testng-selenium 11 | 12 | 13 | 14 | The MIT License (MIT) 15 | https://github.com/jsdevel/java-testng-selenium/LICENSE 16 | 17 | 18 | 19 | 20 | 21 | Joseph Spencer 22 | js.developer.undefined@gmail.com 23 | 24 | 25 | 26 | 27 | scm:git:git@github.com:jsdevel/java-testng-selenium.git 28 | scm:git:git@github.com:jsdevel/java-testng-selenium.git 29 | git@github.com:jsdevel/java-testng-selenium.git 30 | testng-selenium-0.5.0 31 | 32 | 33 | 34 | 1.8 35 | 1.8 36 | UTF-8 37 | UTF-8 38 | https://google.com 39 | 40 | 41 | 42 | 43 | ossrh 44 | https://oss.sonatype.org/content/repositories/snapshots 45 | 46 | 47 | ossrh 48 | https://oss.sonatype.org/service/local/staging/deploy/maven2/ 49 | 50 | 51 | 52 | 53 | 54 | commons-io 55 | commons-io 56 | 1.3.2 57 | 58 | 59 | 60 | net.anthavio 61 | phanbedder-1.9.8 62 | 1.0.0 63 | provided 64 | 65 | 66 | 67 | com.github.detro 68 | phantomjsdriver 69 | 1.2.0 70 | provided 71 | 72 | 73 | 74 | org.seleniumhq.selenium 75 | selenium-java 76 | 2.45.0 77 | provided 78 | 79 | 80 | 81 | org.testng 82 | testng 83 | 6.8.21 84 | provided 85 | 86 | 87 | 88 | 89 | 90 | 91 | org.apache.maven.plugins 92 | maven-surefire-plugin 93 | 2.18.1 94 | 95 | 96 | src/test/resources/testng.xml 97 | 98 | 99 | ${testng.selenium.endpoint} 100 | 101 | 102 | 103 | 104 | 105 | org.apache.maven.plugins 106 | maven-source-plugin 107 | 2.4 108 | 109 | 110 | attach-sources 111 | 112 | jar 113 | 114 | 115 | 116 | 117 | 118 | 119 | org.apache.maven.plugins 120 | maven-javadoc-plugin 121 | 2.10.3 122 | 123 | 124 | attach-javadocs 125 | 126 | jar 127 | 128 | 129 | 130 | 131 | 132 | 133 | org.apache.maven.plugins 134 | maven-release-plugin 135 | 2.5 136 | 137 | true 138 | false 139 | release 140 | deploy 141 | 142 | 143 | 144 | 145 | org.apache.maven.plugins 146 | maven-gpg-plugin 147 | 1.6 148 | 149 | 150 | sign-artifacts 151 | verify 152 | 153 | sign 154 | 155 | 156 | 157 | 158 | 159 | 160 | org.sonatype.plugins 161 | nexus-staging-maven-plugin 162 | 1.6.5 163 | true 164 | 165 | ossrh 166 | https://oss.sonatype.org/ 167 | true 168 | 169 | 170 | 171 | 172 | 173 | -------------------------------------------------------------------------------- /src/main/java/com/github/jsdevel/testng/selenium/config/Config.java: -------------------------------------------------------------------------------- 1 | package com.github.jsdevel.testng.selenium.config; 2 | 3 | import static com.github.jsdevel.testng.selenium.TestNGSeleniumLogger.debug; 4 | import static com.github.jsdevel.testng.selenium.TestNGSeleniumLogger.log; 5 | import java.io.File; 6 | 7 | /** 8 | * This class represents environment configuration understood by 9 | * testng-selenium. Configuration may be taken from a properties file on the 10 | * classpath (testng-selenium.properties), or from system properties. 11 | * 12 | * @see ConfigOptions 13 | * 14 | * @author Joe Spencer 15 | */ 16 | public class Config { 17 | /** 18 | * Enables debug mode. Debug mode will output verbose logging. Recognized 19 | * values are "enabled", "disabled". The default value is "disabled". 20 | * 21 | * @see ConfigOptions#DEBUG 22 | */ 23 | public static final boolean DEBUG = System.getProperty(ConfigOptions.DEBUG, PropertiesConfig.DEBUG).equalsIgnoreCase("enabled"); 24 | 25 | /** 26 | * The WebDriver used for the test run. Possible values are: 27 | * Chrome, Firefox, InternetExplorer, and PhantomJS (the default). 28 | * 29 | * This value may be overridden on a test by test basis with annotations. 30 | * 31 | * @see ConfigOptions#DRIVER 32 | * @see com.github.jsdevel.testng.selenium.annotations.drivers.Chrome 33 | * @see com.github.jsdevel.testng.selenium.annotations.drivers.Firefox 34 | * @see com.github.jsdevel.testng.selenium.annotations.drivers.InternetExplorer 35 | * @see com.github.jsdevel.testng.selenium.annotations.drivers.PhantomJS 36 | */ 37 | public static final String DRIVER = System.getProperty(ConfigOptions.DRIVER, PropertiesConfig.DRIVER); 38 | 39 | /** 40 | * The endpoint used for the test run. By default this is a null value, and 41 | * will cause a failed System.exit() call to occur if null. This value is 42 | * used to resolve desired URLs when requesting page objects from page 43 | * factories. 44 | * 45 | * @see ConfigOptions#ENDPOINT 46 | * @see com.github.jsdevel.testng.selenium.AbstractSuite 47 | */ 48 | public static final String ENDPOINT = System.getProperty(ConfigOptions.ENDPOINT, PropertiesConfig.ENDPOINT); 49 | 50 | /** 51 | * The prefix used when logging. By default this is set to "TestNG-Selenium". 52 | * 53 | * @see ConfigOptions#LOGGING_PREFIX 54 | * @see com.github.jsdevel.testng.selenium.TestNGSeleniumLogger 55 | */ 56 | public static final String LOGGING_PREFIX = System.getProperty(ConfigOptions.LOGGING_PREFIX, PropertiesConfig.LOGGING_PREFIX); 57 | 58 | /** 59 | * The default screen size used for test runs. Possible values are: 60 | * LargeDesktop, Desktop, Tablet, and Phone. The default screen size is 61 | * Phone. 62 | * 63 | * This value may be overridden on a test by test basis with annotations. 64 | * 65 | * @see ConfigOptions#SCREENSIZE 66 | * @see com.github.jsdevel.testng.selenium.annotations.screensizes.LargeDesktop 67 | * @see com.github.jsdevel.testng.selenium.annotations.screensizes.Desktop 68 | * @see com.github.jsdevel.testng.selenium.annotations.screensizes.Tablet 69 | * @see com.github.jsdevel.testng.selenium.annotations.screensizes.Phone 70 | */ 71 | public static final String SCREENSIZE = System.getProperty(ConfigOptions.SCREENSIZE, PropertiesConfig.SCREENSIZE); 72 | 73 | /** 74 | * The temporary directory used to store screen shots, cookie files, and other 75 | * related data needed for test runs. The default value is set to the 76 | * platform specific default temp directory location. 77 | * 78 | * @see ConfigOptions#TMPDIR 79 | */ 80 | public static final String TMPDIR = System.getProperty(ConfigOptions.TMPDIR, PropertiesConfig.TMPDIR); 81 | 82 | /** 83 | * The default User-Agent string to use in the WebDriver. This overrides the 84 | * WebDriver's default User-Agent. 85 | * 86 | * This value may be overridden on a test by test basis with annotations. 87 | * 88 | * @see ConfigOptions#USER_AGENT 89 | * @see com.github.jsdevel.testng.selenium.annotations.driverconfig.UserAgent 90 | */ 91 | public static final String USER_AGENT = System.getProperty(ConfigOptions.USER_AGENT, PropertiesConfig.USER_AGENT); 92 | 93 | private static final String DRIVER_OPTIONS = "Chrome,Firefox,InternetExplorer,PhantomJS"; 94 | private static final String SCREENSIZE_OPTIONS = "LargeDesktop,Desktop,Tablet,Phone"; 95 | 96 | static { 97 | if (!("," + DRIVER_OPTIONS + ",").toLowerCase() 98 | .contains(DRIVER.toLowerCase())) { 99 | log(ConfigOptions.DRIVER + " must be one of " + 100 | DRIVER_OPTIONS); 101 | System.exit(1); 102 | } 103 | 104 | if (ENDPOINT == null) { 105 | log(ConfigOptions.ENDPOINT + 106 | " must be a configured System property!"); 107 | System.exit(1); 108 | } 109 | 110 | if (!("," + SCREENSIZE_OPTIONS + ",").contains("," + SCREENSIZE + ",")) { 111 | log(ConfigOptions.SCREENSIZE + " must be one of " + 112 | SCREENSIZE_OPTIONS); 113 | log("Saw " + SCREENSIZE); 114 | System.exit(1); 115 | } 116 | 117 | if (TMPDIR == null) { 118 | log(ConfigOptions.TMPDIR + 119 | " must be a configured System property!"); 120 | System.exit(1); 121 | } else { 122 | File tmpdir = new File(TMPDIR); 123 | if (tmpdir.exists()) { 124 | if (!tmpdir.isDirectory()) { 125 | log(ConfigOptions.TMPDIR + 126 | " cannot use non directories for tmp dir."); 127 | System.exit(1); 128 | } 129 | } else { 130 | tmpdir.mkdirs(); 131 | } 132 | } 133 | 134 | debug(ConfigOptions.DEBUG + " set to " + DEBUG); 135 | debug(ConfigOptions.DRIVER + " set to " + DRIVER); 136 | debug(ConfigOptions.ENDPOINT + " set to " + ENDPOINT); 137 | debug(ConfigOptions.LOGGING_PREFIX + " set to " + LOGGING_PREFIX); 138 | debug(ConfigOptions.SCREENSIZE + " set to " + SCREENSIZE); 139 | debug(ConfigOptions.TMPDIR + " set to " + TMPDIR); 140 | debug(ConfigOptions.USER_AGENT + " set to " + USER_AGENT); 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /src/main/javadoc/overview.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | TestNG Selenium Overview 6 | 7 | 8 |

Overview

9 | Remove the TestNG Selenium boilerplate. 10 |
    11 |
  1. Overview
  2. 12 |
  3. Project Goals
  4. 13 |
  5. Maven Dependency
  6. 14 |
  7. Writing Suites
  8. 15 |
  9. Writing a Page Factory
  10. 16 |
  11. Writing a Page Object
  12. 17 |
  13. Configuring 18 |
  14. 23 |
24 |

Project Goals

25 |
    26 |
  • Configuration through Property Files, System properties and annotations.
  • 27 |
  • Facilitate running tests in parallel.
  • 28 |
  • Remove typical boilerplate, such as taking screenshots on test 29 | failures, configuring WebDriver, and instantiating page objects.
  • 30 |
  • Declarative way of defining page objects with PageFactory.
  • 31 |
  • Declarative way of configuring tests individually.
  • 32 |
33 |

Maven Dependency

34 | You can add TestNG-Selenium to your existing project as follows: 35 |
36 |
37 | 38 |
39 | For new projects, you can quickly get setup by using the following command at a terminal: 40 |
41 |
42 | 43 |
44 |

Writing Suites

45 | In each of your suite classes, extend AbstractSuite and pass 46 | your PageFactory as a generic type argument. 47 |
48 |
49 | 50 |
51 | AbstractSuite provides a getPageFactory 52 | method to create the page factories passed in as generic type arguments. In 53 | this example, the page factory passed as a generic type on line 7 is 54 | returned by getPageFactory. 55 |

Writing a Page Factory

56 | A PageFactory is nothing more than an interface: 57 |
58 |
59 | 60 |
61 | Each method declared in a PageFactory should return a sub class 62 | of AbstractPage. This allows TestNG-Selenium to do some cool things 63 | when initializing your page objects, like navigating to them when 64 | WebDriver has been created, wiring up annotated fields using 65 | Selenium's PageFactory initializer, and validating that the URL currently 66 | being viewed by WebDriver is valid for the requested page. This approach 67 | also allows you to avoid boilerplate by letting TestNG-Selenium manage your 68 | page factory's lifecycle. 69 |

Writing a Page object.

70 |
    71 |
  1. Create a class I.E. GoogleHomePage
  2. 72 |
  3. Extend com.github.jsdevel.testng.selenium.AbstractPage.
  4. 73 |
  5. Pass your page object's type, and the page factory used to create it 74 | as generic type arguments to AbstractPage.
  6. 75 |
  7. Optionally add WebElement fields annotated with @FindBy (see 76 | Google's 77 | PageFactory pattern.).
  8. 78 |
79 | 80 | If you need to do something before validation occurs, such as wait for 81 | requests, or poll a global javascript variable, you can override 82 | AbstractPage#handlePageInitialized(). 83 |

Configuring

84 | TestNG-Selenium may be configured in one of 3 ways: 85 |
    86 |
  • Properties File
  • 87 |
  • System Properties
  • 88 |
  • Annotations
  • 89 |
90 | In general, annotation based configuration overrides system based 91 | configuration on a per test basis. 92 |

Property File Based Configuration

93 | TestNG-Selenium will look for a file called "testng-selenium.properties" at 94 | the root of your classpath. If found, then values contained therein will 95 | override the default configuration values. The key value pairs are the same 96 | as they are for System Based Configuration. 97 |

System Based Configuration

98 | System based configuration is driven by System properties. System properties 99 | override both properties file and default configuration. 100 |
101 | Here is a list of the system properties recognized by TestNG-Selenium with their default values: 102 |
103 |
104 | 105 |

Annotation Based Configuration

106 | Annotation based configuration can override the default configuration, 107 | properties file annotation, and system based configuration for a single test 108 | run. 109 |
110 | Here is an example of how we can override a system property using an annotation 111 | for a single test run. For the full list of supported annotations, see 112 | package contents under com.github.jsdevel.annotations. 113 |
114 |
115 | 116 | 117 | 118 | -------------------------------------------------------------------------------- /src/main/java/com/github/jsdevel/testng/selenium/AbsractSuiteHelpers.java: -------------------------------------------------------------------------------- 1 | package com.github.jsdevel.testng.selenium; 2 | 3 | import com.github.jsdevel.testng.selenium.annotations.driverconfig.UserAgent; 4 | import com.github.jsdevel.testng.selenium.annotations.drivers.Chrome; 5 | import com.github.jsdevel.testng.selenium.annotations.drivers.Firefox; 6 | import com.github.jsdevel.testng.selenium.annotations.drivers.InternetExplorer; 7 | import com.github.jsdevel.testng.selenium.annotations.screensizes.Desktop; 8 | import com.github.jsdevel.testng.selenium.annotations.screensizes.LargeDesktop; 9 | import com.github.jsdevel.testng.selenium.annotations.screensizes.Phone; 10 | import com.github.jsdevel.testng.selenium.annotations.screensizes.Tablet; 11 | import com.github.jsdevel.testng.selenium.config.Config; 12 | import static com.github.jsdevel.testng.selenium.config.Config.TMPDIR; 13 | import com.github.jsdevel.testng.selenium.exceptions.MissingPageFactoryException; 14 | import java.io.File; 15 | import java.io.IOException; 16 | import java.lang.reflect.Method; 17 | import java.lang.reflect.ParameterizedType; 18 | import java.util.ArrayList; 19 | import java.util.List; 20 | import java.util.logging.Level; 21 | import java.util.logging.Logger; 22 | import net.anthavio.phanbedder.Phanbedder; 23 | import org.apache.commons.io.FileUtils; 24 | import org.openqa.selenium.Dimension; 25 | import org.openqa.selenium.OutputType; 26 | import org.openqa.selenium.TakesScreenshot; 27 | import org.openqa.selenium.WebDriver; 28 | import org.openqa.selenium.chrome.ChromeDriver; 29 | import org.openqa.selenium.firefox.FirefoxDriver; 30 | import org.openqa.selenium.ie.InternetExplorerDriver; 31 | import org.openqa.selenium.phantomjs.PhantomJSDriver; 32 | import org.openqa.selenium.phantomjs.PhantomJSDriverService; 33 | import org.openqa.selenium.remote.DesiredCapabilities; 34 | 35 | /** 36 | * Internal helpers for AbstractSuite. 37 | * 38 | * @author Joe Spencer 39 | */ 40 | class AbsractSuiteHelpers { 41 | private static final File phantomBinary = Phanbedder.unpack(); 42 | static final File SCREENSHOT_DIR; 43 | 44 | static { 45 | SCREENSHOT_DIR = new File(TMPDIR, "screenshots"); 46 | SCREENSHOT_DIR.mkdirs(); 47 | } 48 | 49 | @SuppressWarnings("unchecked") 50 | static void addPageFactory(MethodContextImpl context) { 51 | Class suite = context.method.getDeclaringClass(); 52 | ParameterizedType abstractSuite; 53 | 54 | try { 55 | abstractSuite = (ParameterizedType)suite.getGenericSuperclass(); 56 | } catch (ClassCastException e) { 57 | throw new MissingPageFactoryException( 58 | "AbstractSuite must receive Type parameters I.E. class MySuite " + 59 | "extends AbstractSuite. None were given in " + 60 | suite.getName()); 61 | } 62 | 63 | Class pageFactoryClass = (Class) abstractSuite.getActualTypeArguments()[0]; 64 | context.setPageFactory(PageFactoryProxy.newInstance(pageFactoryClass, context)); 65 | } 66 | 67 | static void addScreensize(MethodContextImpl context) { 68 | Method method = context.method; 69 | WebDriver driver = context.getWebDriver(); 70 | 71 | Dimension testConfiguredDimension = getDimension(method, context); 72 | 73 | if (testConfiguredDimension == null) { 74 | try { 75 | testConfiguredDimension = getDimension(ScreenSizeHelper.class 76 | .getDeclaredMethod(Config.SCREENSIZE.toLowerCase()), context); 77 | } catch (NoSuchMethodException | SecurityException e) { 78 | // this should never get reached. 79 | } 80 | } 81 | 82 | if (testConfiguredDimension != null) { 83 | driver.manage().window().setSize(testConfiguredDimension); 84 | } 85 | } 86 | 87 | static void addUserAgent(MethodContextImpl context) { 88 | Method method = context.method; 89 | if (method.isAnnotationPresent(UserAgent.class)) { 90 | context.setUserAgent(method.getAnnotation(UserAgent.class).value()); 91 | } else if (Config.USER_AGENT != null) { 92 | context.setUserAgent(Config.USER_AGENT); 93 | } 94 | } 95 | 96 | static void addWebDriver(MethodContextImpl context) { 97 | Method method = context.method; 98 | if (method.isAnnotationPresent(Chrome.class) || 99 | Config.DRIVER.equalsIgnoreCase("chrome")) { 100 | addChromeDriver(context); 101 | } else if (method.isAnnotationPresent(Firefox.class) || 102 | Config.DRIVER.equalsIgnoreCase("firefox")) { 103 | addFirefoxDriver(context); 104 | } else if (method.isAnnotationPresent(InternetExplorer.class) || 105 | Config.DRIVER.equalsIgnoreCase("internetexplorer")) { 106 | addInternetExplorerDriver(context); 107 | } else { 108 | addPhantomJSDriver(context); 109 | } 110 | } 111 | 112 | static String getTestName(Method method) { 113 | return method.getDeclaringClass().getName() + ":" + method.getName(); 114 | } 115 | 116 | static void takeScreenshot(MethodContextImpl context) throws IOException { 117 | File screenshotTarget = new File(SCREENSHOT_DIR, getTestName( 118 | context.method) + ".png"); 119 | context.log("Saving a screenshot to " + 120 | screenshotTarget.getAbsolutePath()); 121 | File screenshot = ((TakesScreenshot) context.getWebDriver()) 122 | .getScreenshotAs(OutputType.FILE); 123 | FileUtils.copyFile(screenshot, screenshotTarget); 124 | } 125 | 126 | // Private methods. 127 | private static void addChromeDriver(MethodContextImpl context) { 128 | ChromeDriver driver = new ChromeDriver(); 129 | context.setWebDriver(driver); 130 | } 131 | 132 | private static void addFirefoxDriver(MethodContextImpl context) { 133 | FirefoxDriver driver = new FirefoxDriver(); 134 | context.setWebDriver(driver); 135 | } 136 | 137 | private static void addInternetExplorerDriver(MethodContextImpl context) { 138 | InternetExplorerDriver driver = new InternetExplorerDriver(); 139 | context.setWebDriver(driver); 140 | } 141 | 142 | private static void addPhantomJSDriver(MethodContextImpl context) { 143 | DesiredCapabilities dcaps = new DesiredCapabilities(); 144 | String testName = getTestName(context.method) + "-" + 145 | System.currentTimeMillis(); 146 | dcaps.setCapability(PhantomJSDriverService.PHANTOMJS_EXECUTABLE_PATH_PROPERTY, 147 | phantomBinary.getAbsolutePath()); 148 | List phantomCliArgs = new ArrayList<>(); 149 | phantomCliArgs.add("--web-security=false"); 150 | phantomCliArgs.add("--ignore-ssl-errors=true"); 151 | phantomCliArgs.add("--ssl-protocol=any"); 152 | phantomCliArgs.add("--cookies-file=" + new File(TMPDIR, "cookies-" + 153 | testName + ".txt").getAbsolutePath()); 154 | phantomCliArgs.add("--local-storage-path=" + new File(TMPDIR, 155 | "local-storage-" + testName).getAbsolutePath()); 156 | 157 | List ghostdriverCliArgs = new ArrayList<>(); 158 | ghostdriverCliArgs.add("--logFile=" + new File(TMPDIR, "ghostdriver-" + 159 | testName + ".log").getAbsolutePath()); 160 | 161 | if (!Config.DEBUG) { 162 | phantomCliArgs.add("--webdriver-loglevel=ERROR"); 163 | ghostdriverCliArgs.add("--logLevel=ERROR"); 164 | Logger.getLogger(PhantomJSDriverService.class.getName()).setLevel( 165 | Level.OFF); 166 | } 167 | 168 | dcaps.setCapability(PhantomJSDriverService.PHANTOMJS_CLI_ARGS, 169 | phantomCliArgs.toArray(new String[]{})); 170 | 171 | dcaps.setCapability(PhantomJSDriverService.PHANTOMJS_GHOSTDRIVER_CLI_ARGS, 172 | ghostdriverCliArgs.toArray(new String[]{})); 173 | 174 | if (context.getUserAgent() != null) { 175 | dcaps.setCapability("phantomjs.page.settings.userAgent", context.getUserAgent()); 176 | } 177 | 178 | PhantomJSDriver driver = new PhantomJSDriver(dcaps); 179 | context.setWebDriver(driver); 180 | } 181 | 182 | private static Dimension getDimension(Method method, MethodContextImpl context) { 183 | if (method.isAnnotationPresent(Phone.class)) { 184 | Phone dimension = method.getAnnotation(Phone.class); 185 | context.setScreensize(dimension); 186 | return new Dimension(dimension.width(), dimension.height()); 187 | } else if (method.isAnnotationPresent(Tablet.class)) { 188 | Tablet dimension = method.getAnnotation(Tablet.class); 189 | context.setScreensize(dimension); 190 | return new Dimension(dimension.width(), dimension.height()); 191 | } else if (method.isAnnotationPresent(Desktop.class)) { 192 | Desktop dimension = method.getAnnotation(Desktop.class); 193 | context.setScreensize(dimension); 194 | return new Dimension(dimension.width(), dimension.height()); 195 | } else if (method.isAnnotationPresent(LargeDesktop.class)) { 196 | LargeDesktop dimension = method.getAnnotation(LargeDesktop.class); 197 | context.setScreensize(dimension); 198 | return new Dimension(dimension.width(), dimension.height()); 199 | } 200 | 201 | return null; 202 | } 203 | 204 | private static class ScreenSizeHelper { 205 | @Phone 206 | static void phone(){} 207 | @Tablet 208 | static void tablet(){} 209 | @Desktop 210 | static void deskop(){} 211 | @LargeDesktop 212 | static void lagedeskop(){} 213 | } 214 | } 215 | --------------------------------------------------------------------------------