Skip to content
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

[ATH IMPROVEMENT] Allow local Jenkins instance, simplified API #1484

Merged
merged 23 commits into from
Oct 23, 2017
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
87ecc77
Update ATH tests for new flow
kzantow Oct 12, 2017
33ccc9c
Handle more close cases for errors
kzantow Oct 12, 2017
f8f224d
Some refactoring
kzantow Oct 12, 2017
f070bbe
Add ability to read a properties file for ATH (~/.blueocean-ath-config)
kzantow Oct 17, 2017
78aa546
Add ability to read a properties file for ATH (~/.blueocean-ath-config)
kzantow Oct 17, 2017
f605005
Add ability to read a properties file for ATH (~/.blueocean-ath-config)
kzantow Oct 17, 2017
8d010e5
Whoops
kzantow Oct 17, 2017
1c58498
Whoops, whoops
kzantow Oct 18, 2017
a17c707
Merge remote-tracking branch 'primary/master' into ATH-allow-local-dev
kzantow Oct 18, 2017
97e6cf4
Whoops, whoops
kzantow Oct 18, 2017
04bb80f
Missed a couple things
kzantow Oct 18, 2017
cdd1ed4
Merge remote-tracking branch 'primary/master' into ATH-allow-local-dev
kzantow Oct 18, 2017
972f4a0
Tweaks based on PR feedback
kzantow Oct 18, 2017
d89172d
Consolidate `go` method
kzantow Oct 18, 2017
2201f7a
Consolidate `go` method
kzantow Oct 18, 2017
5cce6ec
Imports
kzantow Oct 18, 2017
4cb6779
Merge remote-tracking branch 'primary/master' into ATH-allow-local-dev
kzantow Oct 19, 2017
aa434f2
Imports
kzantow Oct 20, 2017
c686b05
Merge remote-tracking branch 'primary/master' into ATH-allow-local-dev
kzantow Oct 21, 2017
fced2e0
Use 'improved' click logic for github pipeline create
kzantow Oct 23, 2017
9922950
Merge remote-tracking branch 'primary/master' into ATH-allow-local-dev
kzantow Oct 23, 2017
add4f4a
Typo
kzantow Oct 23, 2017
dbf0fc2
Merge remote-tracking branch 'primary/master' into ATH-allow-local-dev
kzantow Oct 23, 2017
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@ private void runTest(Statement statement, Description description,
eachNotifier.addFailure(e);
} finally {
eachNotifier.fireTestFinished();
LocalDriverElement.close();
}
}

Expand Down
54 changes: 40 additions & 14 deletions acceptance-tests/src/main/java/io/blueocean/ath/AthModule.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,21 @@
import com.google.inject.assistedinject.FactoryModuleBuilder;
import com.google.inject.name.Names;
import com.offbytwo.jenkins.JenkinsServer;
import io.blueocean.ath.factory.ActivityPageFactory;
import io.blueocean.ath.factory.BranchPageFactory;
import io.blueocean.ath.factory.FreestyleJobFactory;
import io.blueocean.ath.factory.MultiBranchPipelineFactory;
import io.blueocean.ath.factory.ClassicPipelineFactory;
import io.blueocean.ath.factory.RunDetailsArtifactsPageFactory;
import io.blueocean.ath.factory.RunDetailsPipelinePageFactory;
import io.blueocean.ath.factory.*;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I seem to recall that using wildcard imports violates some style rules in Jenkins projects, I think @vivek pointed that out to me once. If you're using IDEA and search for "class count to use import with" in settings, you'll find an option. I set it to 100.

import io.blueocean.ath.model.ClassicPipeline;
import io.blueocean.ath.model.FreestyleJob;
import io.blueocean.ath.model.MultiBranchPipeline;
import io.blueocean.ath.model.ClassicPipeline;
import io.blueocean.ath.pages.blue.ActivityPage;
import io.blueocean.ath.pages.blue.BranchPage;
import io.blueocean.ath.pages.blue.RunDetailsArtifactsPage;
import io.blueocean.ath.pages.blue.RunDetailsPipelinePage;
import org.openqa.selenium.Dimension;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.remote.Augmenter;
import org.openqa.selenium.remote.DesiredCapabilities;
import org.openqa.selenium.remote.RemoteWebDriver;

import java.io.File;
import java.io.FileInputStream;
import java.net.URI;
import java.net.URL;
Expand All @@ -33,32 +29,62 @@
public class AthModule extends AbstractModule {
@Override
protected void configure() {
// Can run this locally with system properties, e.g.:
// -DwebDriverType=chrome -DwebDriverUrl=http://localhost:4444 -DwebDriverBrowserSize=800x600 -DjenkinsUrl=http://localhost:8080/jenkins
String webDriverType = System.getProperty("webDriverType");
DesiredCapabilities capability;
if ("chrome".equals(webDriverType)) {
capability = DesiredCapabilities.chrome();
} else {
capability = DesiredCapabilities.firefox();
}

DesiredCapabilities capability = DesiredCapabilities.firefox();
String webDriverUrl = System.getProperty("webDriverUrl", "http://localhost:4444/wd/hub");
String webDriverBrowserSize = System.getProperty("webDriverBrowserSize");

try {
WebDriver driver = new RemoteWebDriver(new URL("http://localhost:4444/wd/hub"), capability);
WebDriver driver = new RemoteWebDriver(new URL(webDriverUrl), capability);
LocalDriverElement.setCurrent(driver);

driver = new Augmenter().augment(driver);
driver.manage().window().maximize();
if (webDriverBrowserSize == null) {
driver.manage().window().maximize();
} else {
String[] widthXHeight = webDriverBrowserSize.split("x");
driver.manage().window().setSize(new Dimension(Integer.parseInt(widthXHeight[0]), Integer.parseInt(widthXHeight[1])));
}
driver.manage().deleteAllCookies();
bind(WebDriver.class).toInstance(driver);

String launchUrl = new String(Files.readAllBytes(Paths.get("runner/.blueocean-ath-jenkins-url")));
String launchUrl = System.getProperty("jenkinsUrl");
if (launchUrl == null) {
launchUrl = new String(Files.readAllBytes(Paths.get("runner/.blueocean-ath-jenkins-url")));
}
bindConstant().annotatedWith(BaseUrl.class).to(launchUrl);

CustomJenkinsServer server = new CustomJenkinsServer(new URI(launchUrl));
CustomJenkinsServer server;
if (System.getProperty("adminUsername") != null) {
server = new CustomJenkinsServer(new URI(launchUrl), System.getProperty("adminUsername"), System.getProperty("adminPassword"));
} else {
server = new CustomJenkinsServer(new URI(launchUrl));
}
bind(JenkinsServer.class).toInstance(server);
bind(CustomJenkinsServer.class).toInstance(server);

if(server.getComputerSet().getTotalExecutors() < 10) {
server.runScript(
"jenkins.model.Jenkins.getInstance().setNumExecutors(10);\n" +
"jenkins.model.Jenkins.getInstance().save();\n");
}

Properties properties = new Properties();
properties.load(new FileInputStream("live.properties"));
File liveProperties = new File("live.properties");
if (liveProperties.canRead()) {
properties.load(new FileInputStream(liveProperties));
}
bind(Properties.class).annotatedWith(Names.named("live")).toInstance(properties);
} catch (Exception e) {
LocalDriverElement.close();
throw new RuntimeException(e);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,14 @@ public class CustomJenkinsServer extends JenkinsServer {
protected final JenkinsHttpClient client;

public CustomJenkinsServer(URI serverUri) {
super(serverUri);
this(serverUri, LoginPage.getUsername(), LoginPage.getPassword());
}

public CustomJenkinsServer(URI serverUri, String username, String password) {
super(serverUri, username, password);
// since JenkinsServer's "client" is private, we must create another one
// use authenticated client so that user's credentials can be accessed
client = new JenkinsHttpClient(serverUri, LoginPage.getUsername(), LoginPage.getPassword());
client = new JenkinsHttpClient(serverUri, username, password);
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,255 @@
package io.blueocean.ath;

import org.openqa.selenium.*;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.FluentWait;

import java.util.List;
import java.util.concurrent.TimeUnit;

/**
* Wrapper around an underlying WebDriver that
* consistently handles waits automatically.
*
* Accepts expressions for css and xpath, if the provided lookup starts with a /, XPath is used
*/
public class LocalDriverElement implements WebElement {
private static ThreadLocal<WebDriver> CURRENT_WEB_DRIVER = new ThreadLocal<>();

protected String expr;

public LocalDriverElement(String expr) {
this.expr = expr;
}

public static void setCurrent(WebDriver driver) {
CURRENT_WEB_DRIVER.set(driver);
}

public static void close() {
WebDriver driver = CURRENT_WEB_DRIVER.get();
if (driver != null) {
try {
driver.close();
} catch(Exception e) {
// ignore, this happens when running individual tests sometimes
}
}
}

/**
* Finds an element by the provided expression {@see LocalDriverElement}
* @param expr css or xpath; if it starts with a /, XPath is used
* @return a new LocalDriverElement
*/
public static LocalDriverElement find(String expr) {
return new LocalDriverElement(expr);
}

/**
* Executes javascript, returns the result
* @param script javascript to execute
* @return the result
*/
public static Object eval(String script) {
return new FluentWait<>(CURRENT_WEB_DRIVER.get())
.pollingEvery(100, TimeUnit.MILLISECONDS)
.withTimeout(WaitUtil.DEFAULT_TIMEOUT, TimeUnit.MILLISECONDS)
.ignoring(NoSuchElementException.class)
.until(ExpectedConditions.jsReturnsValue(script));
}

/**
* Navigates to a specified url
* @param url where to go
*/
public static void go(String url) {
CURRENT_WEB_DRIVER.get().get(url);
}

/**
* Gets a WebDriver instance
* @return from threadlocal
*/
protected WebDriver getDriver() {
return CURRENT_WEB_DRIVER.get();
}

/**
* Gets elements
* @return the elements found
*/
public List<WebElement> getElements() {
By by;
if (expr.startsWith("/")) {
by = By.xpath(expr);
} else {
by = By.cssSelector(expr);
}
return new FluentWait<>(getDriver())
.pollingEvery(100, TimeUnit.MILLISECONDS)
.withTimeout(WaitUtil.DEFAULT_TIMEOUT, TimeUnit.MILLISECONDS)
.ignoring(NoSuchElementException.class)
//.until(ExpectedConditions.visibilityOfAllElementsLocatedBy(by));
.until(ExpectedConditions.numberOfElementsToBeMoreThan(by, 0));
}

/**
* Gets the first matching element
* @return see description
*/
public WebElement getElement() {
return getElements().iterator().next();
}

/**
* Iterates over all found elements with the given function
* @param fn to perform on the elements
*/
public void forEach(java.util.function.Consumer<WebElement> fn) {
for (WebElement e : getElements()) {
fn.accept(e);
}
}

public boolean isVisible() {
return getElement().isDisplayed();
}

@Override
public void click() {
forEach(e -> e.click());
}

@Override
public void submit() {
forEach(e -> e.submit());
}

@Override
public void sendKeys(CharSequence... charSequences) {
forEach(e -> e.sendKeys(charSequences));
}

@Override
public void clear() {
forEach(e -> e.clear());
}

@Override
public String getTagName() {
WebElement el = getElement();
if (el == null) {
return null;
}
return el.getTagName();
}

@Override
public String getAttribute(String s) {
WebElement el = getElement();
if (el == null) {
return null;
}
return el.getAttribute(s);
}

@Override
public boolean isSelected() {
WebElement el = getElement();
if (el == null) {
return false;
}
return el.isSelected();
}

@Override
public boolean isEnabled() {
WebElement el = getElement();
if (el == null) {
return false;
}
return el.isEnabled();
}

@Override
public String getText() {
WebElement el = getElement();
if (el == null) {
return null;
}
return el.getText();
}

@Override
public List<WebElement> findElements(By by) {
WebElement el = getElement();
if (el == null) {
return null;
}
return el.findElements(by);
}

@Override
public WebElement findElement(By by) {
WebElement el = getElement();
if (el == null) {
return null;
}
return el.findElement(by);
}

@Override
public boolean isDisplayed() {
WebElement el = getElement();
if (el == null) {
return false;
}
return el.isDisplayed();
}

@Override
public Point getLocation() {
WebElement el = getElement();
if (el == null) {
return null;
}
return el.getLocation();
}

@Override
public Dimension getSize() {
WebElement el = getElement();
if (el == null) {
return null;
}
return el.getSize();
}

@Override
public Rectangle getRect() {
WebElement el = getElement();
if (el == null) {
return null;
}
return el.getRect();
}

@Override
public String getCssValue(String s) {
WebElement el = getElement();
if (el == null) {
return null;
}
return el.getCssValue(s);
}

@Override
public <X> X getScreenshotAs(OutputType<X> outputType) throws WebDriverException {
WebElement el = getElement();
if (el == null) {
return null;
}
return el.getScreenshotAs(outputType);
}
}
Loading