-
Notifications
You must be signed in to change notification settings - Fork 12
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Run tests in webdriver for CI, both firefox and chrome #14
base: master
Are you sure you want to change the base?
Changes from 2 commits
4d256d3
c9bed43
db89fd7
bafa0d4
fa5176e
8c3b484
3b68889
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,134 @@ | ||
/* | ||
* Copyright © 2019 The GWT Project Authors | ||
* | ||
* 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 org.gwtproject.junit; | ||
|
||
import com.google.gwt.core.ext.TreeLogger; | ||
import com.google.gwt.core.ext.UnableToCompleteException; | ||
import com.google.gwt.junit.JUnitShell; | ||
import com.google.gwt.junit.RunStyle; | ||
import java.net.*; | ||
import java.util.ArrayList; | ||
import java.util.List; | ||
import org.openqa.selenium.Platform; | ||
import org.openqa.selenium.remote.DesiredCapabilities; | ||
import org.openqa.selenium.remote.RemoteWebDriver; | ||
|
||
public class RunStyleWebDriver extends RunStyle { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That class should probably be named RunStyleRemoteWebDriver, as it doesn't support running "local" WebDriver. |
||
|
||
private List<RemoteWebDriver> browsers = new ArrayList<>(); | ||
private Thread keepalive; | ||
|
||
public RunStyleWebDriver(JUnitShell shell) { | ||
super(shell); | ||
} | ||
|
||
@Override | ||
public int initialize(String args) { | ||
if (args == null || args.length() == 0) { | ||
getLogger() | ||
.log( | ||
TreeLogger.ERROR, | ||
"WebDriver runstyle requires a parameter of the form protocol://hostname:port?browser1[,browser2]"); | ||
return -1; | ||
} | ||
String[] parts = args.split("\\?"); | ||
URL remoteAddress = null; | ||
try { | ||
remoteAddress = new URL(parts[0] + "/wd/hub"); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How about letting the user put There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Unless I'm mistaken, Perhaps default to |
||
} catch (MalformedURLException e) { | ||
getLogger().log(TreeLogger.ERROR, e.getMessage(), e); | ||
return -1; | ||
} | ||
|
||
// build each driver based on parts[1].split(",") | ||
String[] browserNames = parts[1].split(","); | ||
|
||
for (String browserName : browserNames) { | ||
DesiredCapabilities capabilities = new DesiredCapabilities(browserName, "", Platform.ANY); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should this possibly read from a JSON file to further configure capabilities? (e.g. allow running against Sauce Labs, etc.) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do you have an example of what such a JSON would look like? I see that DesiredCapabilities can take a Proxy looks like the only property in the default impls that actually has any structure, and it accepts a Map as well, so perhaps it is safe to just deserialize JSON to a recursive map. My main concern then is parameterizing this, passing a path when you just need a browser seems excessive, and passing a path as a string seems messy too (since runStyle can only take a single string, which you then parse yourself, etc). What do you think of supporting multiple arg patterns, one which roughly matches the current setup (just specify URL and browsers), and another for a config file? Perhaps two concrete types, to allow specifying which format will be used by the runstyle name, but shared implementation. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It might be possible to have a single run style that supports everything with, say something like
(I used But maybe it's still better to have separate run styles 🤷♂️ Disclosure: I don't know Selenium/WebDriver much and never actually used them; only saw several FLOSS projects using WebDriver (from JS) with such configurations as JSON (or JS objects). |
||
|
||
try { | ||
RemoteWebDriver wd = new RemoteWebDriver(remoteAddress, capabilities); | ||
browsers.add(wd); | ||
} catch (Exception exception) { | ||
getLogger().log(TreeLogger.ERROR, "Failed to find desired browser", exception); | ||
return -1; | ||
} | ||
} | ||
|
||
Runtime.getRuntime() | ||
.addShutdownHook( | ||
new Thread( | ||
() -> { | ||
if (keepalive != null) { | ||
keepalive.interrupt(); | ||
} | ||
for (RemoteWebDriver browser : browsers) { | ||
try { | ||
browser.quit(); | ||
} catch (Exception ignored) { | ||
// ignore, we're shutting down, continue shutting down others | ||
} | ||
} | ||
})); | ||
return browsers.size(); | ||
} | ||
|
||
@Override | ||
public void launchModule(String moduleName) throws UnableToCompleteException { | ||
// since WebDriver.get is blocking, start a keepalive thread first | ||
keepalive = | ||
new Thread( | ||
() -> { | ||
while (true) { | ||
try { | ||
Thread.sleep(1000); | ||
} catch (InterruptedException e) { | ||
break; | ||
} | ||
for (RemoteWebDriver browser : browsers) { | ||
browser.getTitle(); // as in RunStyleSelenium, simple way to poll the browser | ||
} | ||
} | ||
}); | ||
keepalive.setDaemon(true); | ||
keepalive.start(); | ||
for (RemoteWebDriver browser : browsers) { | ||
browser.get(shell.getModuleUrl(moduleName)); | ||
} | ||
} | ||
|
||
/** | ||
* Work-around until GWT's JUnitShell handles IPv6 addresses correctly. | ||
* https://groups.google.com/d/msg/google-web-toolkit/jLGhwUrKVRY/eQaDO6EUqdYJ | ||
*/ | ||
public String getLocalHostName() { | ||
String host = System.getProperty("webdriver.test.host"); | ||
if (host != null) { | ||
return host; | ||
} | ||
InetAddress a; | ||
try { | ||
a = InetAddress.getLocalHost(); | ||
} catch (UnknownHostException e) { | ||
throw new RuntimeException("Unable to determine my ip address", e); | ||
} | ||
if (a instanceof Inet6Address) { | ||
return "[" + a.getHostAddress() + "]"; | ||
} else { | ||
return a.getHostAddress(); | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
https://errorprone.info/bugpattern/WildcardImport