diff --git a/pom.xml b/pom.xml index fd85c81b6a..055196ee7a 100644 --- a/pom.xml +++ b/pom.xml @@ -492,6 +492,10 @@ and -Djdk.net.URLClassPath.disableClassPathURLCheck=true + + + target/credentials.hpi + @@ -765,6 +769,12 @@ and ${jenkins.version} war + + org.jenkins-ci.plugins + credentials + 1561.v172b_32e210ef + hpi + ${project.build.directory} true diff --git a/src/main/java/org/jenkinsci/test/acceptance/junit/WithCredentials.java b/src/main/java/org/jenkinsci/test/acceptance/junit/WithCredentials.java index 7a60279306..110c300e4f 100644 --- a/src/main/java/org/jenkinsci/test/acceptance/junit/WithCredentials.java +++ b/src/main/java/org/jenkinsci/test/acceptance/junit/WithCredentials.java @@ -15,7 +15,6 @@ import org.jenkinsci.test.acceptance.plugins.credentials.UserPwdCredential; import org.jenkinsci.test.acceptance.plugins.ssh_credentials.SshPrivateKeyCredential; import org.jenkinsci.test.acceptance.po.Jenkins; -import org.junit.AssumptionViolatedException; import org.junit.rules.TestRule; import org.junit.runner.Description; import org.junit.runners.model.Statement; @@ -107,19 +106,13 @@ private boolean enterCredentials(WithCredentials wp) { * @param sshKeyPath path to the ssh key */ private void addSshUsernamePrivateKeyCredentials(String username, String sshKeyPath, String id) { - try { - CredentialsPage cp = new CredentialsPage(jenkins, ManagedCredentials.DEFAULT_DOMAIN); - cp.open(); - SshPrivateKeyCredential sc = cp.add(SshPrivateKeyCredential.class); - sc.username.set(username); - sc.selectEnterDirectly() - .privateKey - .set(resource(sshKeyPath).asText()); - maybeSetId(sc, id); - cp.create(); - } catch (Exception ex) { - throw new AssumptionViolatedException("@WithCredentials requires credentials@2.0.7.", ex); - } + CredentialsPage cp = new CredentialsPage(jenkins, ManagedCredentials.DEFAULT_DOMAIN); + cp.open(); + SshPrivateKeyCredential sc = cp.add(SshPrivateKeyCredential.class); + sc.username.set(username); + sc.selectEnterDirectly().privateKey.set(resource(sshKeyPath).asText()); + maybeSetId(sc, id); + cp.create(); } /** @@ -128,17 +121,13 @@ private void addSshUsernamePrivateKeyCredentials(String username, String sshKeyP * @param password password */ private void addUsernamePasswordCredentials(String username, String password, String id) { - try { - CredentialsPage c = new CredentialsPage(jenkins, ManagedCredentials.DEFAULT_DOMAIN); - c.open(); - final UserPwdCredential upc = c.add(UserPwdCredential.class); - upc.username.set(username); - upc.password.set(password); - maybeSetId(upc, id); - c.create(); - } catch (Exception ex) { - throw new AssumptionViolatedException("@WithCredentials requires credentials@2.0.7.", ex); - } + CredentialsPage c = new CredentialsPage(jenkins, ManagedCredentials.DEFAULT_DOMAIN); + c.open(); + final UserPwdCredential upc = c.add(UserPwdCredential.class); + upc.username.set(username); + upc.password.set(password); + maybeSetId(upc, id); + c.create(); } private void maybeSetId(BaseStandardCredentials creds, String id) { diff --git a/src/main/java/org/jenkinsci/test/acceptance/plugins/credentials/AbstractCredentialsTest.java b/src/main/java/org/jenkinsci/test/acceptance/plugins/credentials/AbstractCredentialsTest.java index f758cc7540..b46ae527dc 100644 --- a/src/main/java/org/jenkinsci/test/acceptance/plugins/credentials/AbstractCredentialsTest.java +++ b/src/main/java/org/jenkinsci/test/acceptance/plugins/credentials/AbstractCredentialsTest.java @@ -110,6 +110,7 @@ protected T createCredentials( SshPrivateKeyCredential castedCred = (SshPrivateKeyCredential) cred; castedCred.description.set(CRED_DSCR); if (scope != null) { + waitFor(by.option(scope)); castedCred.scope.select(scope); } castedCred.username.set(CRED_USER); @@ -126,7 +127,7 @@ private void navigateToCreateCredentials() { tryCredentialsClick(); waitFor(by.href("/user/" + CREATED_USER + "/credentials/store/user")).click(); waitFor(by.href("domain/_")).click(); - waitFor(by.href("newCredentials")).click(); + waitFor(by.button("Add Credentials")).click(); waitFor(by.name("_.id")); } diff --git a/src/main/java/org/jenkinsci/test/acceptance/plugins/credentials/Credential.java b/src/main/java/org/jenkinsci/test/acceptance/plugins/credentials/Credential.java index eddf49240c..be89f7de93 100644 --- a/src/main/java/org/jenkinsci/test/acceptance/plugins/credentials/Credential.java +++ b/src/main/java/org/jenkinsci/test/acceptance/plugins/credentials/Credential.java @@ -29,11 +29,11 @@ protected Credential(PageArea area, String relativePath) { */ public void add() { WebElement dialog = find(by.id("credentials-dialog-form")); - WebElement we = find(submitButton()); + WebElement we = find(by.id("cr-dialog-submit")); we.click(); // wait for the form to be removed from the UI waitFor(driver).until(ExpectedConditions.invisibilityOf(dialog)); - // the notification bar can place itslef over other elements + // the notification bar can place itself over other elements // so wait for it to be added and then disappear waitFor(waitFor(By.id("notification-bar"))).until(bar -> !bar.isDisplayed()); } diff --git a/src/main/java/org/jenkinsci/test/acceptance/plugins/credentials/CredentialsPage.java b/src/main/java/org/jenkinsci/test/acceptance/plugins/credentials/CredentialsPage.java index f60d26f84e..dfca558b1b 100644 --- a/src/main/java/org/jenkinsci/test/acceptance/plugins/credentials/CredentialsPage.java +++ b/src/main/java/org/jenkinsci/test/acceptance/plugins/credentials/CredentialsPage.java @@ -10,7 +10,9 @@ import org.jenkinsci.test.acceptance.po.Control; import org.jenkinsci.test.acceptance.po.Folder; import org.jenkinsci.test.acceptance.po.Jenkins; +import org.jenkinsci.test.acceptance.selenium.Scroller; import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebElement; public class CredentialsPage extends ConfigurablePageObject { public final Control addButton = control(by.xpath("//select[contains(@class, 'setting-input dropdownList')] | " @@ -22,26 +24,41 @@ public class CredentialsPage extends ConfigurablePageObject { * Create a new Credential */ public CredentialsPage(Jenkins j, String domainName) { - super(j, j.url("credentials/store/system/domain/" + domainName + "/newCredentials")); + super(j, j.url("credentials/store/system/domain/" + domainName)); } /** * Create a new Credential scoped to a Folder */ public CredentialsPage(Folder f, String domainName) { - super(f, f.url("credentials/store/folder/domain/" + domainName + "/newCredentials")); + super(f, f.url("credentials/store/folder/domain/" + domainName)); } /** * Create a new personal Credential */ public CredentialsPage(Jenkins j, String domainName, String userName) { - super(j, j.url(String.format("user/%s/credentials/store/user/domain/%s/newCredentials", userName, domainName))); + super(j, j.url(String.format("user/%s/credentials/store/user/domain/%s", userName, domainName))); } public T add(Class type) { - addButton.selectDropdownMenuAlt(type); - String path = find(by.name("credentials")).getAttribute("path"); + WebElement radio = findCaption(type, caption -> { + for (WebElement webElement : all(by.css(".jenkins-choice-list__item__label"))) { + if (webElement.getText().equals(caption)) { + webElement.click(); + return webElement.findElement(by.xpath("./../input")); + } + } + return null; + }); + + String path = radio.getAttribute("path"); + + WebElement nextButton = find(by.id("cr-dialog-next")); + nextButton.click(); + waitFor(by.id("cr-dialog-submit")); + new Scroller(driver).disableStickyElements(); + return newInstance(type, this, path); } @@ -56,7 +73,7 @@ public void setConfigUrl(String url) throws MalformedURLException { } public void create() { - find(by.name("Submit")).click(); + find(by.id("cr-dialog-submit")).click(); assertThat(driver, not(hasContent("This page expects a form submission"))); } @@ -72,9 +89,10 @@ public void delete() { @Override public WebDriver open() { WebDriver wd = super.open(); - // wait for default form fields to be present to avoid possible race - // condition when changing credentials type too fast (happens rarely) - waitFor(by.name("_.id")); + + clickButton("Add Credentials"); + // Selenium will execute the next step before the options have loaded if we don't wait for them + waitFor(by.css(".jenkins-choice-list__item__label")); return wd; } diff --git a/src/main/java/org/jenkinsci/test/acceptance/plugins/ssh_credentials/SshCredentialDialog.java b/src/main/java/org/jenkinsci/test/acceptance/plugins/ssh_credentials/SshCredentialDialog.java index 95c4533e72..2394742ec5 100644 --- a/src/main/java/org/jenkinsci/test/acceptance/plugins/ssh_credentials/SshCredentialDialog.java +++ b/src/main/java/org/jenkinsci/test/acceptance/plugins/ssh_credentials/SshCredentialDialog.java @@ -1,7 +1,6 @@ package org.jenkinsci.test.acceptance.plugins.ssh_credentials; import org.jenkinsci.test.acceptance.plugins.credentials.BaseStandardCredentials; -import org.jenkinsci.test.acceptance.plugins.credentials.Credential; import org.jenkinsci.test.acceptance.po.Control; import org.jenkinsci.test.acceptance.po.PageObject; @@ -16,19 +15,4 @@ public class SshCredentialDialog extends BaseStandardCredentials { public SshCredentialDialog(PageObject context, String path) { super(context, path); } - - /** - * Selects the credential type and bind the controls to the page area. - */ - public T select(Class type) { - - findCaption(type, new Resolver() { - @Override - protected void resolve(String caption) { - kind.select(caption); - } - }); - - return newInstance(type, getPage(), getPath()); - } } diff --git a/src/main/java/org/jenkinsci/test/acceptance/plugins/ssh_slaves/SshSlaveLauncher.java b/src/main/java/org/jenkinsci/test/acceptance/plugins/ssh_slaves/SshSlaveLauncher.java index 540dd04b74..4a68fd3576 100644 --- a/src/main/java/org/jenkinsci/test/acceptance/plugins/ssh_slaves/SshSlaveLauncher.java +++ b/src/main/java/org/jenkinsci/test/acceptance/plugins/ssh_slaves/SshSlaveLauncher.java @@ -4,14 +4,16 @@ import edu.umd.cs.findbugs.annotations.CheckForNull; import java.time.Duration; +import org.jenkinsci.test.acceptance.plugins.credentials.Credential; import org.jenkinsci.test.acceptance.plugins.credentials.UserPwdCredential; -import org.jenkinsci.test.acceptance.plugins.ssh_credentials.SshCredentialDialog; import org.jenkinsci.test.acceptance.plugins.ssh_credentials.SshPrivateKeyCredential; import org.jenkinsci.test.acceptance.po.ComputerLauncher; import org.jenkinsci.test.acceptance.po.Control; import org.jenkinsci.test.acceptance.po.Describable; import org.jenkinsci.test.acceptance.po.PageObject; +import org.jenkinsci.test.acceptance.selenium.Scroller; import org.jenkinsci.test.acceptance.selenium.UselessFileDetectorReplacement; +import org.openqa.selenium.WebElement; /** * @author Kohsuke Kawaguchi @@ -32,14 +34,31 @@ public SshSlaveLauncher(PageObject context, String path) { super(context, path); } - public SshCredentialDialog addCredential() { + public T addCredential(Class type) { find(by.button("Add")).click(); - find(by.css(".jenkins-dropdown")) - .findElement(by.button("Jenkins Credentials Provider")) - .click(); + find(by.css(".jenkins-dropdown")).findElement(by.button("Global")).click(); - return new SshCredentialDialog(getPage(), "/credentials"); + // Selenium will execute the next step before the options have loaded if we don't wait for them + waitFor(by.css(".jenkins-choice-list__item__label")); + + WebElement radio = findCaption(type, caption -> { + for (WebElement webElement : all(by.css(".jenkins-choice-list__item__label"))) { + if (webElement.getText().equals(caption)) { + webElement.click(); + return webElement.findElement(by.xpath("./../input")); + } + } + return null; + }); + + String path = radio.getAttribute("path"); + + WebElement nextButton = find(by.id("cr-dialog-next")); + nextButton.click(); + waitFor(by.id("cr-dialog-submit")); + new Scroller(driver).disableStickyElements(); + return newInstance(type, getPage(), path); } public void setJavaPath(String jvmPath) { @@ -55,6 +74,8 @@ public SshSlaveLauncher port(int port) { } private void ensureAdvancedOpen() { + new Scroller(driver).disableStickyElements(); + control("advanced-button").click(); } @@ -66,8 +87,7 @@ private void ensureAdvancedOpen() { * @return the SshSlaveLauncher to be configured */ public SshSlaveLauncher pwdCredentials(String username, String password) { - final SshCredentialDialog dia = this.addCredential(); - final UserPwdCredential cred = dia.select(UserPwdCredential.class); + final UserPwdCredential cred = this.addCredential(UserPwdCredential.class); cred.username.set(username); cred.password.set(password); // credentials are identified by their id. Set username as id so it can be found by it @@ -86,8 +106,7 @@ public SshSlaveLauncher pwdCredentials(String username, String password) { * @return the SshSlaveLauncher to be configured */ public SshSlaveLauncher pwdCredentials(String username, String password, String id) { - final SshCredentialDialog dia = this.addCredential(); - final UserPwdCredential cred = dia.select(UserPwdCredential.class); + final UserPwdCredential cred = this.addCredential(UserPwdCredential.class); cred.username.set(username); cred.password.set(password); // credentials are identified by their id. @@ -105,8 +124,7 @@ public SshSlaveLauncher pwdCredentials(String username, String password, String * @return the SshSlaveLauncher to be configured */ public SshSlaveLauncher keyCredentials(String username, String key, @CheckForNull String passphrase) { - final SshCredentialDialog dia = this.addCredential(); - final SshPrivateKeyCredential cred = dia.select(SshPrivateKeyCredential.class); + final SshPrivateKeyCredential cred = this.addCredential(SshPrivateKeyCredential.class); cred.username.set(username); if (passphrase != null) { cred.passphrase.set(passphrase); diff --git a/src/main/resources/org/jenkinsci/test/acceptance/selenium/disable-sticky-elements.js b/src/main/resources/org/jenkinsci/test/acceptance/selenium/disable-sticky-elements.js index 525c2f3f17..51766d73c1 100644 --- a/src/main/resources/org/jenkinsci/test/acceptance/selenium/disable-sticky-elements.js +++ b/src/main/resources/org/jenkinsci/test/acceptance/selenium/disable-sticky-elements.js @@ -7,6 +7,20 @@ const bottomAppBar = document.getElementById("bottom-sticker"); // https://github.com/jenkinsci/jenkins/commit/6481e78d20a0c689859058da5a029489e8b5072c introduced a shadow on a different div!? const bottomShadow = document.querySelector(".jenkins-bottom-app-bar__shadow") +document.querySelectorAll(".bottom-sticker-inner") + .forEach(element => { + // there can be multiple bottom stickers (e.g. in a dialog) but there's no class on the actual element + const bottomSticker = element.parentNode + if (bottomSticker) { + bottomSticker.style.position = "relative"; + } + }) + +document.querySelectorAll(".jenkins-bottom-app-bar__shadow") + .forEach(element => { + element.style.position = "initial"; + }) + if (header) { header.style.position = "relative"; } diff --git a/src/test/java/plugins/SshSlavesPluginTest.java b/src/test/java/plugins/SshSlavesPluginTest.java index aa7ed5f69d..c75bc37bce 100644 --- a/src/test/java/plugins/SshSlavesPluginTest.java +++ b/src/test/java/plugins/SshSlavesPluginTest.java @@ -41,7 +41,6 @@ import org.jenkinsci.test.acceptance.junit.WithPlugins; import org.jenkinsci.test.acceptance.plugins.credentials.CredentialsPage; import org.jenkinsci.test.acceptance.plugins.credentials.ManagedCredentials; -import org.jenkinsci.test.acceptance.plugins.ssh_credentials.SshCredentialDialog; import org.jenkinsci.test.acceptance.plugins.ssh_credentials.SshPrivateKeyCredential; import org.jenkinsci.test.acceptance.plugins.ssh_slaves.SshSlaveLauncher; import org.jenkinsci.test.acceptance.po.Control; @@ -96,14 +95,13 @@ public void newAgent() { // ignore } - SshCredentialDialog f = l.addCredential(); + SshPrivateKeyCredential sc = l.addCredential(SshPrivateKeyCredential.class); { - SshPrivateKeyCredential sc = f.select(SshPrivateKeyCredential.class); sc.description.set(description); sc.username.set(username); sc.selectEnterDirectly().privateKey.set(privateKey); + sc.add(); } - f.add(); l.credentialsId.select(String.format("%s (%s)", username, description)); }