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

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

merged 23 commits into from
Oct 23, 2017

Conversation

kzantow
Copy link
Collaborator

@kzantow kzantow commented Oct 12, 2017

Adding ability to run the ATH locally against a locally running dev instance.

esp. @imeredith

@@ -93,7 +94,7 @@ public void setLogEvents(boolean logEvents) {
public void connect() throws UnirestException, InterruptedException {
SecureRandom rnd = new SecureRandom();
String clientId = "ath-" + rnd.nextLong();
HttpResponse<JsonNode> httpResponse = Unirest.get(baseUrl + "/sse-gateway/connect?clientId=" + clientId).asJson();
HttpResponse<JsonNode> httpResponse = Unirest.get(baseUrl + "/sse-gateway/connect?clientId=" + clientId).basicAuth(LoginPage.getUsername(), LoginPage.getPassword()).asJson();
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

might need to add some logic when to do this

Copy link
Member

Choose a reason for hiding this comment

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

if running unauthed I guess it may ignore those basic headers anyway (maybe...)

Copy link
Collaborator

Choose a reason for hiding this comment

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

We can set the logged in user in the state fairly easily (when using @Login)

return ExpectedConditions.urlToBe(base + "/").apply(driver);
});
logger.info("Logged in as alice");
find("input[name=j_username]").sendKeys(getUsername());
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This was a 30 minute example of how my prior testing frameworks looked... well, similar.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Only thing I would put here is that I would make find use "By" class. Ie find(By.id(..)), good otherwise. Could default to use css for strings though

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I suppose it doesn't hurt to have an override, but I would definitely not use By as the only way to specify things since you can specify a find by id using css like #my-fancy-id, which would work with this already. The only thing missing is find by link text, which you can use xpath for, but is a little more cumbersome. We really shouldn't be finding anything by text in these tests, anyway, tho

@michaelneale
Copy link
Member

nice

@kzantow kzantow changed the title [WiP] Allow local ATH run/dev [WiP] Allow local Jenkins instance with ATH run/dev Oct 12, 2017
@@ -27,7 +27,7 @@ public static void clearText(WebElement element) {
*/
public static void setText(WebElement element, String text) {
checkTextElement(element);
element.sendKeys(Keys.chord(Keys.CONTROL, "a"));
element.clear();//sendKeys(Keys.chord(Keys.CONTROL, "a"));
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

is there a reason it was doing CTRL-a instead of clear before? this definitely doesn't work on Mac ;)

Copy link
Member

Choose a reason for hiding this comment

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

ctrl-a goes back to the start of a text cell (emacs keys - may they never die) which I gather is not the intent here?

Copy link
Contributor

Choose a reason for hiding this comment

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

Sorry, somehow I completely missed the presence of clear() in the docs. That is much easier. :)

logger.info("Logged in as alice");
find("input[name=j_username]").sendKeys(getUsername());
find("input[name=j_password]").sendKeys(getPassword());
find("//button[contains(text(), 'log')]").click();
Copy link
Contributor

Choose a reason for hiding this comment

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

I like this shorthand overall. Per discussion in Gitter, I think we want a static click method that implements the same retry logic as here: https://github.com/jenkinsci/blueocean-plugin/pull/1412/files#diff-9d7946f8ccd8a4191146940351b51999

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yeah, absolutely. This is why I said it was a 30 minute implementation. The real logic typically ends up being more complex, and yeah, definitely agree what we do is add utility methods like click with retry and setText which should be doing what sendKeys is doing now, e.g. you just want to set the text of an input, but you may want to send individual keys for other purposes e.g. testing an autocomplete

@cliffmeyers
Copy link
Contributor

I like where this PR is headed although I would like to land #1412 first. We'd also need to check to make sure that the changes to make ATH run better in Chrome do not fail in Firefox - perhaps we can do that in this PR?

Down the road we probably want to take a look at Selenium Grid for running multiple browsers, maybe as part of the weekly CI builds we do now? https://github.com/SeleniumHQ/selenium/wiki/Grid2

@kzantow
Copy link
Collaborator Author

kzantow commented Oct 12, 2017

@cliffmeyers yes, this is just a bit of an example (and something working that makes it easier to run tests locally - e.g. no need for docker, etc.). Certainly land #1412 first and then look to revisit this and combine the changes in a sensible way moving forward.

@imeredith
Copy link
Collaborator

I like what is going on here.

/**
* Provides utility methods to downstream test classes
*/
public class BasePage {
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This should be named something else...

Copy link
Contributor

Choose a reason for hiding this comment

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

Perhaps BasePageObject or AbstractPageObject and made abstract if not meant to be instantiated directly.

Copy link
Contributor

@cliffmeyers cliffmeyers left a comment

Choose a reason for hiding this comment

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

Left some feedback that hopefully spurs further discussion.

We definitely want to work that click retry logic into the appropriate spot. Then I think shift our API usage to click(".some-css-classname") rather than find(".some-css-classname").click() unless it makes sense to bake the retry impl into the WebElement itself, in which case "trailing click" usage would be fine? Without having done this myself or checked out the code and debugged through it I'm having trouble knowing what's the correct route to go.

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.

/**
* Provides utility methods to downstream test classes
*/
public class BasePage {
Copy link
Contributor

Choose a reason for hiding this comment

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

Perhaps BasePageObject or AbstractPageObject and made abstract if not meant to be instantiated directly.

* Navigates to a relative url to the base url
* @param url where to go
*/
public void go(String url) {
Copy link
Contributor

Choose a reason for hiding this comment

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

I like the idea of two methods to navigate to relative and absolute paths, although it's not clear which one is which from the names go and goTo

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

agreed, maybe could just inspect the url if it starts with http(s) then don't use the base

}

@Override
public void submit() {
Copy link
Contributor

Choose a reason for hiding this comment

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

We don't really have forms that truly submit in the app, should we just remove this?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

it's necessary to implement the interface


@Override
public void click() {
forEach(e -> e.click());
Copy link
Contributor

Choose a reason for hiding this comment

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

Do want to support clicking multiple elements? It seems that it could have unintended consequences if the selector is too loose. Perhaps warn if multiple elements are matched?

We should bake in the retry logic from WaitUtil.click here I believe.


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

Choose a reason for hiding this comment

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

Same question as in click

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

yeah, probably not, might be good to dump a warning for those; i'll make those changes - or throw an error?

}

@Override
public boolean isEnabled() {
Copy link
Contributor

Choose a reason for hiding this comment

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

Would this style of method implicitly wait for the element to be enabled, or do an immediate check for whether it's enabled? If the latter, it seems like it could be subject to timing failures. Is there logic we can borrow from ExpectedConditions here? I honestly don't know the answer but the stuff in WaitUtil generally seems to wait the right way, although maybe the API can be improved here?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

just checks for it being enabled. sometimes you might want to check for things enabled/disabled based on user input. could add more methods as needed e.g. waitUntilEnabled

}

@Override
public String getText() {
Copy link
Contributor

Choose a reason for hiding this comment

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

Similar question as above, say for example the textToBePresentInElement method on ExpectedConditions seems useful here. Again I'm a little foggy on these API's as to whether that's appropriate in the WebElement impl or if we need a util method on BasePage instead? Sorry if I'm muddying the waters.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

i really hope we can't find an element without text while in the background somehow it gets filled in. that kind of behavior would be a bit ridiculous, even for react. these types of things we can at least identify and bake in to this API as needed instead of cluttering up the tests with all kinds of wait conditions

@kzantow kzantow changed the title [WiP] Allow local Jenkins instance with ATH run/dev [ATH IMPROVEMENT] Allow local Jenkins instance, simplified API Oct 21, 2017
@michaelneale
Copy link
Member

I think this is ok if no one else has objections, not sure what the details are though - perhaps README could be updated?

@@ -105,7 +103,7 @@ public void selectPipelineToCreate(String pipeline){
}

public void clickCreatePipelineButton() {
wait.until(ExpectedConditions.elementToBeClickable(createPipelineButton)).click();
click(".button-create");
Copy link
Member

Choose a reason for hiding this comment

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

looks nice

@michaelneale
Copy link
Member

FWIW: #1503 seemed to pass (fixing the click/co-ordinates problem) but will leave that one in favour of this if this one is better and works.

@michaelneale
Copy link
Member

michaelneale commented Oct 23, 2017

I was trying this locally - testEditor fails for me (times out) - is @cliffmeyers or @kzantow able to run it locally? (master + https://github.com/jenkinsci/blueocean-plugin/pull/1503/files does work for me though). Not sure if intermittent or something else, but I am wary of changes if I can't run them locally

EDIT: after a few tries it is ok (my machine struggles) - so I think this may be LGTM

I think @cliffmeyers is ok with it? woudl be good to get master stable @kzantow

@michaelneale
Copy link
Member

michaelneale commented Oct 23, 2017

yep 🐝 from me.

I had too many things running...

@sophistifunk
Copy link
Collaborator

Is this the fix for the missing button?

@kzantow
Copy link
Collaborator Author

kzantow commented Oct 23, 2017

@sophistifunk this is a number of improvements to ATH and that fix, yes

@sophistifunk
Copy link
Collaborator

Sweet, when it's merged I'll pull it into my branch in the quest for fields verdant.

@michaelneale
Copy link
Member

@sophistifunk yes this will fix it

public WebElement getElement() throws NoSuchElementException {
List<WebElement> elements = getElements();
if (elements == null || elements.isEmpty()) {
throw new NoSuchElementException("Nothing matched to click on for: " + expr);
Copy link
Contributor

Choose a reason for hiding this comment

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

The reference to "click" here might be copy/paste error?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

ah yes, will fix; oops

logger.debug("exception: " + ex.getMessage());
try {
// typically this is during an animation, which should not take long
Thread.sleep(500);
Copy link
Contributor

Choose a reason for hiding this comment

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

I had assumed I'd need to add a sleep to this method, although it worked very reliably without it (at least in my local env). I suppose there's no harm in being more conservative by having it pause longer before retry.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Well, what I'd like to do (and maybe what I did before, I don't quite remember) is get rid of the WebDriver wait and replace it with some smart polling / retries of our own. It should speed things up in some cases. We'll want to add some other utilities to try to have some explicit waits for a css selector, for example waiting for github to scan repos and such, we know that will take a while.

@@ -2031,6 +2032,21 @@ public void testBlockedStep() throws Exception {
assertEquals(1, stepsResp.size());
assertEquals("QUEUED", stepsResp.get(0).get("state"));
}
} else {
// Avoid spurious code coverage failures
Copy link
Contributor

Choose a reason for hiding this comment

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

Nice, had these fail from time to time myself... 👍

Copy link
Contributor

@cliffmeyers cliffmeyers left a comment

Choose a reason for hiding this comment

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

Change all LGTM 🐝

@kzantow
Copy link
Collaborator Author

kzantow commented Oct 23, 2017

... merging once green :)

@kzantow kzantow mentioned this pull request Oct 23, 2017
@kzantow
Copy link
Collaborator Author

kzantow commented Oct 23, 2017

Last 3 runs all green, I think this fixes the latest flakiness on master.

@kzantow kzantow merged commit c2c5134 into jenkinsci:master Oct 23, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants