/*
* Copyright 2016 GoDataDriven B.V.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.divolte.server;
import com.google.common.collect.ImmutableList;
import com.google.common.primitives.Ints;
import io.divolte.server.ServerTestUtils.TestServer;
import org.junit.After;
import org.junit.AssumptionViolatedException;
import org.junit.Rule;
import org.junit.rules.TestRule;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameter;
import org.junit.runners.Parameterized.Parameters;
import org.junit.runners.model.Statement;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebDriverException;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.phantomjs.PhantomJSDriver;
import org.openqa.selenium.remote.DesiredCapabilities;
import org.openqa.selenium.remote.RemoteWebDriver;
import javax.annotation.Nullable;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Collection;
import java.util.Collections;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import static io.divolte.server.BrowserLists.*;
@RunWith(Parameterized.class)
public abstract class SeleniumTestBase {
public static final String DRIVER_ENV_VAR = "SELENIUM_DRIVER";
public static final String PHANTOMJS_DRIVER = "phantomjs";
public static final String CHROME_DRIVER = "chrome";
public static final String SAUCE_DRIVER = "sauce";
public static final String BS_DRIVER = "browserstack";
public static final String SAUCE_USER_NAME_ENV_VAR = "SAUCE_USER_NAME";
public static final String SAUCE_API_KEY_ENV_VAR = "SAUCE_API_KEY";
public static final String SAUCE_HOST_ENV_VAR = "SAUCE_HOST";
public static final String SAUCE_PORT_ENV_VAR = "SAUCE_PORT";
public static final String BS_USER_NAME_ENV_VAR = "BS_USER_NAME";
public static final String BS_API_KEY_ENV_VAR = "BS_API_KEY";
public static final String CHROME_DRIVER_LOCATION_ENV_VAR = "CHROME_DRIVER";
public static final DesiredCapabilities LOCAL_RUN_CAPABILITIES;
static {
LOCAL_RUN_CAPABILITIES = new DesiredCapabilities();
LOCAL_RUN_CAPABILITIES.setBrowserName("Local Selenium instructed browser");
}
@Rule
public final TestRule suppressWebDriverNavigationExceptions = (base, description) -> new Statement() {
@Override
public void evaluate() throws Throwable {
try {
base.evaluate();
} catch (final WebDriverException e) {
if (e.getMessage().contains("history navigation does not work")) {
throw new AssumptionViolatedException("Selenium driver doesn't support navigation required for this test.", e);
}
throw e;
}
}
};
@Nullable
protected WebDriver driver;
@Nullable
protected TestServer server;
@Parameter(0)
public Supplier<DesiredCapabilities> capabilities;
@Parameter(1)
public String capabilityDescription;
@Parameter(2)
public boolean quirksMode;
@Parameters(name = "Selenium JS test: {1} (quirks-mode={2})")
public static Iterable<Object[]> sauceLabBrowsersToTest() {
final Collection<Object[]> browserList;
if (!System.getenv().containsKey(DRIVER_ENV_VAR)) {
browserList = Collections.emptyList();
} else if (SAUCE_DRIVER.equals(System.getenv().get(DRIVER_ENV_VAR))) {
browserList = SAUCE_BROWSER_LIST;
System.out.println("Selenium test running on SauceLabs with these browsers:\n" + browserNameList(SAUCE_BROWSER_LIST));
} else if (BS_DRIVER.equals(System.getenv().get(DRIVER_ENV_VAR))) {
browserList = BS_BROWSER_LIST;
System.out.println("Selenium test running on BrowserStack with these browsers:\n" + browserNameList(BS_BROWSER_LIST));
} else {
// Parameters are not used for non-sauce tests
browserList = ImmutableList.of(new Object[] {
(Supplier<DesiredCapabilities>) () -> LOCAL_RUN_CAPABILITIES, "Local JS test run"
});
}
// For each browser, we need to run in and out of quirks mode.
return browserList.stream()
.flatMap((browser) ->
ImmutableList.of(new Object[] { browser[0], browser[1], false },
new Object[] { browser[0], browser[1], true }).stream())
.collect(Collectors.toList());
}
public enum TEST_PAGES {
BASIC("test-basic-page"),
BASIC_COPY("test-basic-page-copy"),
PAGE_VIEW_SUPPLIED("test-basic-page-provided-pv-id"),
CUSTOM_JAVASCRIPT_NAME("test-custom-javascript-name"),
CUSTOM_PAGE_VIEW("test-custom-page-view");
private final String resourceName;
TEST_PAGES(final String resourceName) {
this.resourceName = Objects.requireNonNull(resourceName);
}
}
protected String urlOf(final TEST_PAGES page) {
final String modeString = quirksMode ? "quirks" : "strict";
return String.format("http://127.0.0.1:%d/%s/%s.html",
server.port, modeString, page.resourceName);
}
protected void doSetUp(final String configFileName) throws Exception {
doSetUp(Optional.of(configFileName));
}
protected void doSetUp() throws Exception {
doSetUp(Optional.empty());
}
private void doSetUp(final Optional<String> configFileName) throws Exception {
final String driverName = System.getenv().getOrDefault(DRIVER_ENV_VAR, PHANTOMJS_DRIVER);
switch (driverName) {
case CHROME_DRIVER:
setupLocalChrome();
break;
case SAUCE_DRIVER:
setupSauceLabs();
break;
case BS_DRIVER:
setupBrowserStack();
break;
case PHANTOMJS_DRIVER:
default:
driver = new PhantomJSDriver();
break;
}
server = configFileName.map(TestServer::new).orElseGet(TestServer::new);
}
private void setupBrowserStack() throws MalformedURLException {
final String bsUserName = Optional
.ofNullable(System.getenv(BS_USER_NAME_ENV_VAR))
.orElseThrow(() -> new RuntimeException("When using 'browserstack' as Selenium driver, please set the BrowserStack username "
+ "in the " + BS_USER_NAME_ENV_VAR + " env var."));
final String bsApiKey = Optional
.ofNullable(System.getenv(BS_API_KEY_ENV_VAR))
.orElseThrow(() -> new RuntimeException("When using 'browserstack' as Selenium driver, please set the BrowserStack username "
+ "in the " + BS_API_KEY_ENV_VAR + " env var."));
final DesiredCapabilities caps = capabilities.get();
caps.setCapability("job-name", "Selenium JS test: " + capabilityDescription);
driver = new RemoteWebDriver(
new URL(String.format("http://%s:%s@hub.browserstack.com/wd/hub", bsUserName, bsApiKey)),
caps);
}
private void setupSauceLabs() throws MalformedURLException {
final String sauceUserName = Optional
.ofNullable(System.getenv(SAUCE_USER_NAME_ENV_VAR))
.orElseThrow(() -> new RuntimeException("When using 'sauce' as Selenium driver, please set the SauceLabs username "
+ "in the " + SAUCE_USER_NAME_ENV_VAR + " env var."));
final String sauceApiKey = Optional
.ofNullable(System.getenv(SAUCE_API_KEY_ENV_VAR))
.orElseThrow(() -> new RuntimeException("When using 'sauce' as Selenium driver, please set the SauceLabs username "
+ "in the " + SAUCE_API_KEY_ENV_VAR + " env var."));
final String sauceHost = Optional
.ofNullable(System.getenv(SAUCE_HOST_ENV_VAR))
.orElse("localhost");
final int saucePort = Optional
.ofNullable(System.getenv(SAUCE_PORT_ENV_VAR)).map(Ints::tryParse)
.orElse(4445);
final DesiredCapabilities caps = capabilities.get();
caps.setCapability("job-name", "Selenium JS test: " + capabilityDescription);
driver = new RemoteWebDriver(
new URL(String.format("http://%s:%s@%s:%d/wd/hub", sauceUserName, sauceApiKey, sauceHost, saucePort)),
caps);
}
private void setupLocalChrome() {
System.setProperty("webdriver.chrome.driver",
Optional.ofNullable(System.getenv(CHROME_DRIVER_LOCATION_ENV_VAR))
.orElseThrow(
() -> new RuntimeException("When using 'chrome' as Selenium driver, please set the location of the "
+ "Chrome driver manager server thingie in the env var: " + CHROME_DRIVER_LOCATION_ENV_VAR)));
driver = new ChromeDriver();
}
@After
public void tearDown() {
if (null != driver) {
driver.quit();
driver = null;
}
if (null != server) {
server.server.shutdown();
server = null;
}
}
}