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 | *
85 | * - Set internal values, such as context, endpoint, url etc.
86 | * - Validate the syntax of the desired {@link URL}.
87 | * - If the {@link WebDriver} associated with this page is currently viewing
88 | * about:blank (normal when a WebDriver is first instantiated), then it loads
89 | * the desired URL.
90 | * - Initializes the page with
91 | * {@link org.openqa.selenium.support.PageFactory}.
92 | * - Calls {@link #handlePageInitialized()} to optionally do something once
93 | * the AbstractPage has been initialized. This would be a good time to wait
94 | * for the page to get fully setup.
95 | * - Calls {@link #isPageViewableFrom(java.net.URL)} to verify that the
96 | * desired URL represents this {@link Page}.
97 | *
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 | - Overview
8 | - Project Goals
9 | - Maven Dependency
10 | - Writing Suites
11 | - Writing a Page Factory
12 | - Writing a Page Object
13 | - Configuring
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 | - Create a class I.E. GoogleHomePage
70 | - Extend com.github.jsdevel.testng.selenium.AbstractPage.
71 | - Pass your page object's type, and the page factory used to create it
72 | as generic type arguments to AbstractPage.
73 | - Optionally add WebElement fields annotated with @FindBy (see
74 | Google's
75 | PageFactory pattern.).
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 | - Overview
12 | - Project Goals
13 | - Maven Dependency
14 | - Writing Suites
15 | - Writing a Page Factory
16 | - Writing a Page Object
17 | - Configuring
18 |
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 | - Create a class I.E. GoogleHomePage
72 | - Extend com.github.jsdevel.testng.selenium.AbstractPage.
73 | - Pass your page object's type, and the page factory used to create it
74 | as generic type arguments to AbstractPage.
75 | - Optionally add WebElement fields annotated with @FindBy (see
76 | Google's
77 | PageFactory pattern.).
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 |
--------------------------------------------------------------------------------