Skip to content
Permalink
Browse files

Merge pull request #787 from codeborne/rosolko-784

Fix #784 and close #785
  • Loading branch information...
asolntsev committed Aug 27, 2018
2 parents fe32335 + 1fa0ba0 commit 2a928a0a117d1743cb2eea7e723ed08fa53582ba
Showing with 587 additions and 118 deletions.
  1. +1 −1 config/checkstyle/checkstyle.xml
  2. +25 −0 src/main/java/com/codeborne/selenide/AuthenticationType.java
  3. +34 −0 src/main/java/com/codeborne/selenide/Credentials.java
  4. +41 −9 src/main/java/com/codeborne/selenide/Selenide.java
  5. +58 −24 src/main/java/com/codeborne/selenide/impl/Navigator.java
  6. +35 −0 src/main/java/com/codeborne/selenide/proxy/AuthenticationFilter.java
  7. +6 −3 src/main/java/com/codeborne/selenide/proxy/SelenideProxyServer.java
  8. +4 −2 src/test/java/com/codeborne/selenide/WebDriverRunnerTest.java
  9. +112 −2 src/test/java/com/codeborne/selenide/impl/NavigatorTest.java
  10. +50 −0 src/test/java/com/codeborne/selenide/proxy/AuthenticationFilterTest.java
  11. +48 −1 src/test/java/integration/BasicAuthTest.java
  12. +5 −7 src/test/java/integration/FileDownloadViaHttpGetTest.java
  13. +1 −3 src/test/java/integration/FileDownloadViaProxyTest.java
  14. +12 −1 src/test/java/integration/IntegrationTest.java
  15. +1 −1 src/test/java/integration/proxy/ProxyServerUsageTest.java
  16. +45 −9 src/test/java/integration/server/BaseHandler.java
  17. +21 −10 src/test/java/integration/server/BasicAuthHandler.java
  18. +8 −8 src/test/java/integration/server/BasicAuthSecurityHandler.java
  19. +9 −14 src/test/java/integration/server/FileDownloadHandler.java
  20. +5 −14 src/test/java/integration/server/FileRenderHandler.java
  21. +5 −8 src/test/java/integration/server/FileUploadHandler.java
  22. +28 −0 src/test/java/integration/server/HeadersPrinterHandler.java
  23. +5 −1 src/test/java/integration/server/LocalHttpServer.java
  24. +28 −0 src/test/java/integration/server/Result.java
@@ -57,7 +57,7 @@
<module name="UnnecessaryParentheses"/>
<!--<module name="MutableException"/>-->
<module name="ClassFanOutComplexity">
<property name="max" value="28"/>
<property name="max" value="30"/>
</module>
<module name="CyclomaticComplexity">
<property name="max" value="20"/>
@@ -0,0 +1,25 @@
package com.codeborne.selenide;

/**
* Authentication schemes.
*
* @see <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication#Authentication_schemes">Web HTTP reference</a>
*/
public enum AuthenticationType {
BASIC("Basic"),
BEARER("Bearer"),
DIGEST("Digest"),
HOBA("HOBA"),
MUTUAL("Mutual"),
AWS4_HMAC_SHA256("AWS4-HMAC-SHA256");

private final String value;

AuthenticationType(String value) {
this.value = value;
}

public String getValue() {
return value;
}
}
@@ -0,0 +1,34 @@
package com.codeborne.selenide;

import java.util.Base64;

import static java.nio.charset.StandardCharsets.UTF_8;

public final class Credentials {
public final String login;
public final String password;

public Credentials(String login, String password) {
this.login = login;
this.password = password;
}

/**
* The resulting string is base64 encoded (YWxhZGRpbjpvcGVuc2VzYW1l).
*
* @return encoded string
*/
public String encode() {
byte[] credentialsBytes = combine().getBytes(UTF_8);
return Base64.getEncoder().encodeToString(credentialsBytes);
}

private String combine() {
return String.format("%s:%s", login, password);
}

@Override
public String toString() {
return combine();
}
}
@@ -83,24 +83,56 @@ public static void open(URL absoluteUrl) {

/**
* The main starting point in your tests.
* <p>
* Open a browser window with given URL and credentials for basic authentication
*
* <p>
* If browser window was already opened before, it will be reused.
*
* <p>
* Don't bother about closing the browser - it will be closed automatically when all your tests are done.
*
* @param relativeOrAbsoluteUrl
* @param domain
* @param login
* @param password
* If not starting with "http://" or "https://" or "file://", it's considered to be relative URL.
* In this case, it's prepended by baseUrl
* <p>
* If not starting with "http://" or "https://" or "file://", it's considered to be relative URL.
* <p>
* In this case, it's prepended by baseUrl
*/
public static void open(String relativeOrAbsoluteUrl, String domain, String login, String password) {
navigator.open(relativeOrAbsoluteUrl, domain, login, password);
mockModalDialogs();
}

/**
* The main starting point in your tests.
* <p>
* Open browser and pass authentication using build-in proxy.
* <p>
* A common authenticationType is "Basic". See Web HTTP reference for other types.
* <p>
* This method can only work if - {@code Configuration.fileDownload == Configuration.FileDownloadMode.PROXY;}
*
* @see <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Proxy-Authorization">Web HTTP reference</a>
* @see AuthenticationType
*/
public static void open(String relativeOrAbsoluteUrl, AuthenticationType authenticationType, String login, String password) {
Credentials credentials = new Credentials(login, password);
open(relativeOrAbsoluteUrl, authenticationType, credentials);
}

/**
* The main starting point in your tests.
* <p>
* Open browser and pass authentication using build-in proxy.
* <p>
* A common authenticationType is "Basic". See Web HTTP reference for other types.
* <p>
* This method can only work if - {@code Configuration.fileDownload == Configuration.FileDownloadMode.PROXY;}
*
* @see <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Proxy-Authorization">Web HTTP reference</a>
* @see AuthenticationType
* @see Credentials
*/
public static void open(String relativeOrAbsoluteUrl, AuthenticationType authenticationType, Credentials credentials) {
navigator.open(relativeOrAbsoluteUrl, authenticationType, credentials);
}

/**
* @see Selenide#open(URL, String, String, String)
*/
@@ -1,17 +1,24 @@
package com.codeborne.selenide.impl;

import com.codeborne.selenide.AuthenticationType;
import com.codeborne.selenide.Configuration;
import com.codeborne.selenide.Credentials;
import com.codeborne.selenide.logevents.SelenideLog;
import com.codeborne.selenide.logevents.SelenideLogger;
import com.codeborne.selenide.proxy.AuthenticationFilter;
import com.codeborne.selenide.proxy.SelenideProxyServer;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebDriverException;

import java.net.URL;
import java.util.logging.Logger;

import static com.codeborne.selenide.Configuration.FileDownloadMode.PROXY;
import static com.codeborne.selenide.Configuration.baseUrl;
import static com.codeborne.selenide.Configuration.captureJavascriptErrors;
import static com.codeborne.selenide.WebDriverRunner.getAndCheckWebDriver;
import static com.codeborne.selenide.WebDriverRunner.getSelenideProxy;
import static com.codeborne.selenide.WebDriverRunner.getWebDriver;
import static com.codeborne.selenide.WebDriverRunner.isIE;
import static com.codeborne.selenide.logevents.LogEvent.EventStatus.PASS;
@@ -20,54 +27,67 @@
private static final Logger log = Logger.getLogger(Navigator.class.getName());

public void open(String relativeOrAbsoluteUrl) {
open(relativeOrAbsoluteUrl, "", "", "");
navigateTo(relativeOrAbsoluteUrl, AuthenticationType.BASIC, "", "", "");
}

public void open(URL url) {
open(url, "", "", "");
navigateTo(url.toExternalForm(), AuthenticationType.BASIC, "", "", "");
}

public void open(String relativeOrAbsoluteUrl, String domain, String login, String password) {
if (isAbsoluteUrl(relativeOrAbsoluteUrl)) {
navigateToAbsoluteUrl(relativeOrAbsoluteUrl, domain, login, password);
} else {
navigateToAbsoluteUrl(absoluteUrl(relativeOrAbsoluteUrl), domain, login, password);
}
navigateTo(relativeOrAbsoluteUrl, AuthenticationType.BASIC, domain, login, password);
}

public void open(URL url, String domain, String login, String password) {
navigateToAbsoluteUrl(url.toExternalForm());
navigateTo(url.toExternalForm(), AuthenticationType.BASIC, domain, login, password);
}

protected String absoluteUrl(String relativeUrl) {
return baseUrl + relativeUrl;
public void open(String relativeOrAbsoluteUrl, AuthenticationType authenticationType, Credentials credentials) {
navigateTo(relativeOrAbsoluteUrl, authenticationType, "", credentials.login, credentials.password);
}

protected void navigateToAbsoluteUrl(String url) {
navigateToAbsoluteUrl(url, "", "", "");
private AuthenticationFilter basicAuthRequestFilter() {
getAndCheckWebDriver();
SelenideProxyServer selenideProxy = getSelenideProxy();
return selenideProxy.requestFilter("authentication");
}

protected void navigateToAbsoluteUrl(String url, String domain, String login, String password) {
String absoluteUrl(String relativeOrAbsoluteUrl) {
return isAbsoluteUrl(relativeOrAbsoluteUrl) ? relativeOrAbsoluteUrl : baseUrl + relativeOrAbsoluteUrl;
}

private void navigateTo(String url, AuthenticationType authenticationType, String domain, String login, String password) {
url = absoluteUrl(url);

if (isIE() && !isLocalFile(url)) {
url = makeUniqueUrlToAvoidIECaching(url, System.nanoTime());
}

boolean hasAuthentication = !domain.isEmpty() || !login.isEmpty() || !password.isEmpty();
if (hasAuthentication) {
if (Configuration.fileDownload == PROXY) {
basicAuthRequestFilter().setAuthentication(authenticationType, new Credentials(login, password));
}
else if (authenticationType == AuthenticationType.BASIC) {
url = appendBasicAuthToURL(url, domain, login, password);
}
else {
throw new UnsupportedOperationException("Cannot use " + authenticationType + " authentication without proxy server");
}
}
else {
if (!domain.isEmpty()) domain += "%5C";
if (!login.isEmpty()) login += ":";
if (!password.isEmpty()) password += "@";
int idx1 = url.indexOf("://") + 3;
url = (idx1 < 3 ? "" : (url.substring(0, idx1 - 3) + "://"))
+ domain
+ login
+ password
+ (idx1 < 3 ? url : url.substring(idx1));
if (Configuration.fileDownload == PROXY) {
basicAuthRequestFilter().removeAuthentication();
}
}

SelenideLog log = SelenideLogger.beginStep("open", url);
try {
WebDriver webdriver = getAndCheckWebDriver();
webdriver.navigate().to(url);
collectJavascriptErrors((JavascriptExecutor) webdriver);
if (webdriver instanceof JavascriptExecutor) {
collectJavascriptErrors((JavascriptExecutor) webdriver);
}
SelenideLogger.commitStep(log, PASS);
} catch (WebDriverException e) {
SelenideLogger.commitStep(log, e);
@@ -85,6 +105,20 @@ protected void navigateToAbsoluteUrl(String url, String domain, String login, St
}
}

String appendBasicAuthToURL(String url, String domain, String login, String password) {
if (!domain.isEmpty()) domain += "%5C";
if (!login.isEmpty()) login += ":";
if (!password.isEmpty()) password += "@";
int index = url.indexOf("://") + 3;
if (index < 3) return domain + login + password + url;

return url.substring(0, index - 3) + "://"
+ domain
+ login
+ password
+ url.substring(index);
}

protected void collectJavascriptErrors(JavascriptExecutor webdriver) {
if (!captureJavascriptErrors) return;

@@ -125,7 +159,7 @@ boolean isAbsoluteUrl(String relativeOrAbsoluteUrl) {
isLocalFile(relativeOrAbsoluteUrl);
}

protected boolean isLocalFile(String url) {
private boolean isLocalFile(String url) {
return url.toLowerCase().startsWith("file:");
}

@@ -0,0 +1,35 @@
package com.codeborne.selenide.proxy;

import com.codeborne.selenide.AuthenticationType;
import com.codeborne.selenide.Credentials;
import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.HttpRequest;
import io.netty.handler.codec.http.HttpResponse;
import net.lightbody.bmp.filters.RequestFilter;
import net.lightbody.bmp.util.HttpMessageContents;
import net.lightbody.bmp.util.HttpMessageInfo;

public class AuthenticationFilter implements RequestFilter {
private AuthenticationType authenticationType;
private Credentials credentials;

@Override
public HttpResponse filterRequest(HttpRequest request, HttpMessageContents contents, HttpMessageInfo messageInfo) {
if (authenticationType != null) {
String authorization = String.format("%s %s", authenticationType.getValue(), credentials.encode());
HttpHeaders headers = request.headers();
headers.add("Authorization", authorization);
headers.add("Proxy-Authorization", authorization);
}
return null;
}

public void setAuthentication(AuthenticationType authenticationType, Credentials credentials) {
this.authenticationType = authenticationType;
this.credentials = credentials;
}

public void removeAuthentication() {
setAuthentication(null, null);
}
}
@@ -69,6 +69,7 @@ public void start() {
proxy.setChainedProxy(getProxyAddress(outsideProxy));
}

addRequestFilter("authentication", new AuthenticationFilter());
addRequestFilter("requestSizeWatchdog", new RequestSizeWatchdog());
addResponseFilter("responseSizeWatchdog", new ResponseSizeWatchdog());
addResponseFilter("download", new FileDownloadFilter());
@@ -77,22 +78,24 @@ public void start() {
port = proxy.getPort();
}


/**
* Add a custom request filter which allows to track/modify all requests from browser to server
*
* @param name unique name of filter
* @param requestFilter the filter
*/
public void addRequestFilter(String name, RequestFilter requestFilter) {
if (requestFilters.containsKey(name)) {
if (isRequestFilterAdded(name)) {
throw new IllegalArgumentException("Duplicate request filter: " + name);
}

proxy.addRequestFilter(requestFilter);
requestFilters.put(name, requestFilter);
}

private boolean isRequestFilterAdded(String name) {
return requestFilters.containsKey(name);
}

/**
* Add a custom response filter which allows to track/modify all server responses to browser
*
@@ -1,7 +1,5 @@
package com.codeborne.selenide;

import java.net.URL;

import com.codeborne.selenide.extension.MockWebDriverExtension;
import com.codeborne.selenide.impl.WebDriverThreadLocalContainer;
import org.assertj.core.api.WithAssertions;
@@ -16,6 +14,9 @@
import org.openqa.selenium.remote.RemoteWebDriver;
import org.openqa.selenium.support.events.WebDriverEventListener;

import java.net.URL;

import static com.codeborne.selenide.Configuration.FileDownloadMode.HTTPGET;
import static com.codeborne.selenide.Selenide.open;
import static com.codeborne.selenide.WebDriverRunner.FIREFOX;
import static com.codeborne.selenide.WebDriverRunner.HTMLUNIT;
@@ -42,6 +43,7 @@ void resetWebDriverContainer() {

WebDriverRunner.webdriverContainer = spy(new WebDriverThreadLocalContainer());
doReturn(null).when((JavascriptExecutor) driver).executeScript(anyString(), any());
Configuration.fileDownload = HTTPGET;
}

@AfterEach
Oops, something went wrong.

0 comments on commit 2a928a0

Please sign in to comment.
You can’t perform that action at this time.