-
Notifications
You must be signed in to change notification settings - Fork 530
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
Conversation
@@ -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(); |
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.
might need to add some logic when to do this
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.
if running unauthed I guess it may ignore those basic headers anyway (maybe...)
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.
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()); |
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.
This was a 30 minute example of how my prior testing frameworks looked... well, similar.
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.
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
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.
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
nice |
@@ -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")); |
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.
is there a reason it was doing CTRL-a
instead of clear
before? this definitely doesn't work on Mac ;)
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.
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?
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.
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(); |
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.
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
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.
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
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 |
@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. |
I like what is going on here. |
/** | ||
* Provides utility methods to downstream test classes | ||
*/ | ||
public class BasePage { |
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.
This should be named something else...
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.
Perhaps BasePageObject
or AbstractPageObject
and made abstract if not meant to be instantiated directly.
# Conflicts: # acceptance-tests/src/main/java/io/blueocean/ath/AthModule.java
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.
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.*; |
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.
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 { |
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.
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) { |
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.
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
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.
agreed, maybe could just inspect the url if it starts with http(s)
then don't use the base
} | ||
|
||
@Override | ||
public void submit() { |
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.
We don't really have forms that truly submit in the app, should we just remove this?
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.
it's necessary to implement the interface
|
||
@Override | ||
public void click() { | ||
forEach(e -> e.click()); |
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.
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)); |
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.
Same question as in click
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.
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() { |
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.
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?
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.
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() { |
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.
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.
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.
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
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"); |
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.
looks nice
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. |
I was trying this 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 |
yep 🐝 from me. I had too many things running... |
Is this the fix for the missing button? |
@sophistifunk this is a number of improvements to ATH and that fix, yes |
Sweet, when it's merged I'll pull it into my branch in the quest for fields verdant. |
@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); |
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.
The reference to "click" here might be copy/paste error?
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.
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); |
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.
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.
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.
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 |
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.
Nice, had these fail from time to time myself... 👍
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.
Change all LGTM 🐝
... merging once green :) |
Last 3 runs all green, I think this fixes the latest flakiness on master. |
Adding ability to run the ATH locally against a locally running dev instance.
esp. @imeredith