From 7ace9debd3aca4788e94df630afe580aa98969ef Mon Sep 17 00:00:00 2001 From: Cliff Meyers Date: Thu, 21 Sep 2017 15:58:29 -0400 Subject: [PATCH 01/30] use chrome instead of firefox and enable console logs integration --- acceptance-tests/runner/scripts/start-selenium.sh | 2 +- .../src/main/java/io/blueocean/ath/AthModule.java | 9 ++++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/acceptance-tests/runner/scripts/start-selenium.sh b/acceptance-tests/runner/scripts/start-selenium.sh index 6d89b65f301..082375dff2e 100755 --- a/acceptance-tests/runner/scripts/start-selenium.sh +++ b/acceptance-tests/runner/scripts/start-selenium.sh @@ -6,7 +6,7 @@ $SCRIPT_DIR/stop-selenium.sh echo "" echo " Starting Selenium Docker container..." echo "" -docker run -d --name blueo-selenium -p 15900:5900 -p 4444:4444 -e no_proxy=localhost selenium/standalone-firefox-debug:2.53.0 > /dev/null +docker run -d --name blueo-selenium -p 15900:5900 -p 4444:4444 -e no_proxy=localhost selenium/standalone-chrome-debug:2.53.0 > /dev/null # Output the containers bridge network IP to file SELENIUM_IP=`docker inspect -f '{{ .NetworkSettings.IPAddress }}' blueo-selenium` diff --git a/acceptance-tests/src/main/java/io/blueocean/ath/AthModule.java b/acceptance-tests/src/main/java/io/blueocean/ath/AthModule.java index 58facb507cf..00ad953262c 100644 --- a/acceptance-tests/src/main/java/io/blueocean/ath/AthModule.java +++ b/acceptance-tests/src/main/java/io/blueocean/ath/AthModule.java @@ -19,7 +19,10 @@ import io.blueocean.ath.pages.blue.RunDetailsArtifactsPage; import io.blueocean.ath.pages.blue.RunDetailsPipelinePage; import org.openqa.selenium.WebDriver; +import org.openqa.selenium.logging.LogType; +import org.openqa.selenium.logging.LoggingPreferences; import org.openqa.selenium.remote.Augmenter; +import org.openqa.selenium.remote.CapabilityType; import org.openqa.selenium.remote.DesiredCapabilities; import org.openqa.selenium.remote.RemoteWebDriver; @@ -29,12 +32,16 @@ import java.nio.file.Files; import java.nio.file.Paths; import java.util.Properties; +import java.util.logging.Level; public class AthModule extends AbstractModule { @Override protected void configure() { - DesiredCapabilities capability = DesiredCapabilities.firefox(); + DesiredCapabilities capability = DesiredCapabilities.chrome(); + LoggingPreferences logPrefs = new LoggingPreferences(); + logPrefs.enable(LogType.BROWSER, Level.ALL); + capability.setCapability(CapabilityType.LOGGING_PREFS, logPrefs); try { WebDriver driver = new RemoteWebDriver(new URL("http://localhost:4444/wd/hub"), capability); From 4002984786f17e50803dbeaf35d894d767f7c59f Mon Sep 17 00:00:00 2001 From: Cliff Meyers Date: Thu, 21 Sep 2017 16:00:06 -0400 Subject: [PATCH 02/30] direct Selenium Chrome's console logs to log4j --- .../java/io/blueocean/ath/ATHJUnitRunner.java | 16 ++++++- .../java/io/blueocean/ath/LogEntryLogger.java | 47 +++++++++++++++++++ 2 files changed, 62 insertions(+), 1 deletion(-) create mode 100644 acceptance-tests/src/main/java/io/blueocean/ath/LogEntryLogger.java diff --git a/acceptance-tests/src/main/java/io/blueocean/ath/ATHJUnitRunner.java b/acceptance-tests/src/main/java/io/blueocean/ath/ATHJUnitRunner.java index 094c9af1bc3..7cbeaada093 100644 --- a/acceptance-tests/src/main/java/io/blueocean/ath/ATHJUnitRunner.java +++ b/acceptance-tests/src/main/java/io/blueocean/ath/ATHJUnitRunner.java @@ -18,6 +18,8 @@ import org.openqa.selenium.OutputType; import org.openqa.selenium.TakesScreenshot; import org.openqa.selenium.WebDriver; +import org.openqa.selenium.logging.LogEntries; +import org.openqa.selenium.logging.LogEntry; import org.openqa.selenium.remote.ScreenshotException; import java.io.File; @@ -75,8 +77,10 @@ public void evaluate() throws Throwable { try { next.evaluate(); + outputConsoleLogs(); } catch (Exception e) { writeScreenShotCause(e, test, method); + outputConsoleLogs(); throw e; } @@ -114,7 +118,16 @@ private void writeScreenShotCause(Throwable t, Object test, FrameworkMethod meth FileUtils.copyFile(scrFile, file); logger.info("Wrote screenshot to " + file.getAbsolutePath()); } - } + } + + private void outputConsoleLogs() { + logger.info("browser console output below:"); + WebDriver driver = injector.getInstance(WebDriver.class); + LogEntries logs = driver.manage().logs().get("browser"); + for (LogEntry entry : logs) { + LogEntryLogger.recordLogEntry(entry); + } + } @Override @@ -174,4 +187,5 @@ public synchronized Throwable fillInStackTrace() { return this; } } + } diff --git a/acceptance-tests/src/main/java/io/blueocean/ath/LogEntryLogger.java b/acceptance-tests/src/main/java/io/blueocean/ath/LogEntryLogger.java new file mode 100644 index 00000000000..d0548953e97 --- /dev/null +++ b/acceptance-tests/src/main/java/io/blueocean/ath/LogEntryLogger.java @@ -0,0 +1,47 @@ +package io.blueocean.ath; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.apache.log4j.Logger; +import org.openqa.selenium.logging.LogEntry; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +/** + * Writes a Selenium LogEntry to log4j + * @author cliffmeyers + */ +class LogEntryLogger { + private static Logger logger = Logger.getLogger(LogEntryLogger.class); + + private static final ObjectMapper jsonMapper = new ObjectMapper(); + private static final TypeReference> typeRef = new TypeReference>() {}; + + static void recordLogEntry(LogEntry entry) { + if (!logger.isInfoEnabled()) { + return; + } + + String time; + String level; + String text; + + try { + // handle messages written by @jenkins-cd/js-logging + Map messageJson = jsonMapper.readValue(entry.getMessage(), typeRef); + Map message = (Map) messageJson.get("message"); + time = String.valueOf(message.get("timestamp")); + level = String.valueOf(message.get("level")); + text = String.valueOf(message.get("text")); + } catch (IOException e) { + // handle messages written natively by console.error|warn|log|debug + time = String.valueOf(entry.getTimestamp()); + level = String.valueOf(entry.getLevel()); + text = entry.getMessage(); + } + + logger.info(String.format("%s - %s - %s", time, level, text)); + } +} From 4f0b12207611db35dcee6927f8c02ce04af7245c Mon Sep 17 00:00:00 2001 From: Cliff Meyers Date: Thu, 21 Sep 2017 16:16:35 -0400 Subject: [PATCH 03/30] suppress superfluous log entries --- .../main/java/io/blueocean/ath/LogEntryLogger.java | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/acceptance-tests/src/main/java/io/blueocean/ath/LogEntryLogger.java b/acceptance-tests/src/main/java/io/blueocean/ath/LogEntryLogger.java index d0548953e97..2bde72dd580 100644 --- a/acceptance-tests/src/main/java/io/blueocean/ath/LogEntryLogger.java +++ b/acceptance-tests/src/main/java/io/blueocean/ath/LogEntryLogger.java @@ -14,13 +14,12 @@ * @author cliffmeyers */ class LogEntryLogger { - private static Logger logger = Logger.getLogger(LogEntryLogger.class); - + private static final Logger logger = Logger.getLogger(LogEntryLogger.class); private static final ObjectMapper jsonMapper = new ObjectMapper(); private static final TypeReference> typeRef = new TypeReference>() {}; static void recordLogEntry(LogEntry entry) { - if (!logger.isInfoEnabled()) { + if (!logger.isInfoEnabled() || isSuperfluousLogEntry(entry)) { return; } @@ -44,4 +43,13 @@ static void recordLogEntry(LogEntry entry) { logger.info(String.format("%s - %s - %s", time, level, text)); } + + // special handling to suppress some repetitive logging messages that are not helpful + private static final String MESSAGE_JS_LOGGING = "@jenkins-cd/logging is explained"; + private static final String MESSAGE_CHROME_CONSOLE = "Chrome displays console errors"; + + static boolean isSuperfluousLogEntry(LogEntry entry) { + String message = entry.getMessage(); + return message.contains(MESSAGE_JS_LOGGING) || message.contains(MESSAGE_CHROME_CONSOLE); + } } From 60cca2cfce7bbf4135367c858908d0f9161165f0 Mon Sep 17 00:00:00 2001 From: Cliff Meyers Date: Fri, 22 Sep 2017 11:35:54 -0400 Subject: [PATCH 04/30] try to improve error handling / logging code especially for Selenium output --- blueocean-web/src/main/js/ErrorUtils.js | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/blueocean-web/src/main/js/ErrorUtils.js b/blueocean-web/src/main/js/ErrorUtils.js index 0cdf964e585..047a280af2c 100644 --- a/blueocean-web/src/main/js/ErrorUtils.js +++ b/blueocean-web/src/main/js/ErrorUtils.js @@ -22,18 +22,22 @@ function isFirefox() { function logApplicationError(messageOrEvent) { const message = messageOrEvent.error || messageOrEvent; - console.error('Hnhandled Error: ', message); - if (messageOrEvent.preventDefault) { - messageOrEvent.preventDefault(); + if (message && message.stack) { + console.error('Unhandled Error: ', message.stack); + + if (messageOrEvent.preventDefault) { + messageOrEvent.preventDefault(); + } } + // otherwise fall back to any default behavior } function logUnhandledPromiseRejection(errorEvent) { const { reason } = errorEvent.detail || errorEvent; - if (reason) { - console.error('Unhandled Rejection: ', reason); + if (reason && reason.stack) { + console.error('Unhandled Rejection: ', reason.stack); errorEvent.preventDefault(); } // otherwise we'll fall back to the default rejection handler From 2744998bec984920f1e51c3eb19dcf2a71b698d1 Mon Sep 17 00:00:00 2001 From: Cliff Meyers Date: Mon, 25 Sep 2017 16:02:21 -0400 Subject: [PATCH 05/30] another attempt to make Chrome + Selenium error stack readable --- blueocean-web/src/main/js/ErrorUtils.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/blueocean-web/src/main/js/ErrorUtils.js b/blueocean-web/src/main/js/ErrorUtils.js index 047a280af2c..60d4c64e719 100644 --- a/blueocean-web/src/main/js/ErrorUtils.js +++ b/blueocean-web/src/main/js/ErrorUtils.js @@ -24,7 +24,7 @@ function logApplicationError(messageOrEvent) { const message = messageOrEvent.error || messageOrEvent; if (message && message.stack) { - console.error('Unhandled Error: ', message.stack); + console.error('Unhandled Error: ', JSON.stringify(message.stack, null, 4)); if (messageOrEvent.preventDefault) { messageOrEvent.preventDefault(); @@ -37,7 +37,7 @@ function logUnhandledPromiseRejection(errorEvent) { const { reason } = errorEvent.detail || errorEvent; if (reason && reason.stack) { - console.error('Unhandled Rejection: ', reason.stack); + console.error('Unhandled Rejection: ', JSON.stringify(reason.stack, null, 4)); errorEvent.preventDefault(); } // otherwise we'll fall back to the default rejection handler From 4013f1b94836c9e62ea522344afdf41b22067d6c Mon Sep 17 00:00:00 2001 From: Cliff Meyers Date: Tue, 26 Sep 2017 11:42:54 -0400 Subject: [PATCH 06/30] another attempt to make Chrome + Selenium error stack readable --- blueocean-web/src/main/js/ErrorUtils.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/blueocean-web/src/main/js/ErrorUtils.js b/blueocean-web/src/main/js/ErrorUtils.js index 60d4c64e719..5c03259ceb5 100644 --- a/blueocean-web/src/main/js/ErrorUtils.js +++ b/blueocean-web/src/main/js/ErrorUtils.js @@ -23,6 +23,8 @@ function isFirefox() { function logApplicationError(messageOrEvent) { const message = messageOrEvent.error || messageOrEvent; + console.log('Unhandled Error 1: ' + JSON.stringify(messageOrEvent, null, 4)); + if (message && message.stack) { console.error('Unhandled Error: ', JSON.stringify(message.stack, null, 4)); @@ -36,6 +38,8 @@ function logApplicationError(messageOrEvent) { function logUnhandledPromiseRejection(errorEvent) { const { reason } = errorEvent.detail || errorEvent; + console.log('Unhandled Rejection 1: ' + JSON.stringify(errorEvent, null, 4)); + if (reason && reason.stack) { console.error('Unhandled Rejection: ', JSON.stringify(reason.stack, null, 4)); errorEvent.preventDefault(); From f8e73b146f57c2994d3037f6317e923e95a0e5dd Mon Sep 17 00:00:00 2001 From: Cliff Meyers Date: Tue, 3 Oct 2017 11:44:44 -0400 Subject: [PATCH 07/30] make the error logging code a bit more explicit and fall back to logging overall event if object structure is unexpected --- blueocean-web/src/main/js/ErrorUtils.js | 37 +++++++++++++++---------- 1 file changed, 23 insertions(+), 14 deletions(-) diff --git a/blueocean-web/src/main/js/ErrorUtils.js b/blueocean-web/src/main/js/ErrorUtils.js index 5c03259ceb5..470ee224816 100644 --- a/blueocean-web/src/main/js/ErrorUtils.js +++ b/blueocean-web/src/main/js/ErrorUtils.js @@ -21,30 +21,39 @@ function isFirefox() { } function logApplicationError(messageOrEvent) { - const message = messageOrEvent.error || messageOrEvent; - - console.log('Unhandled Error 1: ' + JSON.stringify(messageOrEvent, null, 4)); + let message = null; + + if (messageOrEvent.error && messageOrEvent.error.stack) { + message = messageOrEvent.error.stack; + } else if (messageOrEvent.stack) { + message = messageOrEvent.stack; + } else { + message = messageOrEvent; + } - if (message && message.stack) { - console.error('Unhandled Error: ', JSON.stringify(message.stack, null, 4)); + console.error('Unhandled Error: ', JSON.stringify(message, null, 4)); - if (messageOrEvent.preventDefault) { - messageOrEvent.preventDefault(); - } + if (messageOrEvent.preventDefault) { + messageOrEvent.preventDefault(); } - // otherwise fall back to any default behavior } function logUnhandledPromiseRejection(errorEvent) { - const { reason } = errorEvent.detail || errorEvent; + let message = null; + + if (errorEvent.detail && errorEvent.detail.reason && errorEvent.detail.reason.stack) { + message = errorEvent.detail.reason.stack; + } else if (errorEvent.reason && errorEvent.reason.stack) { + message = errorEvent.reason.stack; + } else { + message = errorEvent; + } - console.log('Unhandled Rejection 1: ' + JSON.stringify(errorEvent, null, 4)); + console.error('Unhandled Rejection: ', JSON.stringify(message, null, 4)); - if (reason && reason.stack) { - console.error('Unhandled Rejection: ', JSON.stringify(reason.stack, null, 4)); + if (errorEvent.preventDefault) { errorEvent.preventDefault(); } - // otherwise we'll fall back to the default rejection handler } function initializeErrorHandling() { From 886516102b7900d7cb465ecf5d1ca7c32ec2230b Mon Sep 17 00:00:00 2001 From: Cliff Meyers Date: Tue, 3 Oct 2017 15:57:41 -0400 Subject: [PATCH 08/30] switch nightwatch config to use chrome --- acceptance-tests/nightwatch.conf.js | 8 ++++---- .../runner/runtime/src/test/java/utils/DevRunner.java | 6 +++--- acceptance-tests/runner/scripts/start-selenium.sh | 2 +- acceptance-tests/src/main/nightwatch.json | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/acceptance-tests/nightwatch.conf.js b/acceptance-tests/nightwatch.conf.js index c52915d542e..dd99a12a832 100644 --- a/acceptance-tests/nightwatch.conf.js +++ b/acceptance-tests/nightwatch.conf.js @@ -3,7 +3,7 @@ module.exports = (function (settings) { var url = require('url'); var launchUrl; var netaddr = require('network-address'); - + if (process.env.LAUNCH_URL) { // // This allows you to run the tests against a Jenkins instance of @@ -29,7 +29,7 @@ module.exports = (function (settings) { launchUrl = fs.readFileSync(jenkins_url_file, 'utf8'); } - + // Replace localhost addresses with the actual IP, allowing it // to work inside a docker container running on the host. launchUrl = launchUrl.replace('localhost', netaddr()); @@ -46,7 +46,7 @@ module.exports = (function (settings) { console.log('Jenkins running at: ' + settings.test_settings.default.launch_url); console.log(" NOTE:"); - console.log(" Selenium and the browser (Firefox) are running in a docker"); + console.log(" Selenium and the browser (Chrome) are running in a docker"); console.log(" container that also has VNC. This allows you to connect if"); console.log(" you'd like to look at the browser while the tests run."); console.log(" Simple run:"); @@ -59,6 +59,6 @@ module.exports = (function (settings) { if (fs.existsSync('target/.selenium_server_provided')) { settings.selenium.start_process = false; } - + return settings; })(require('./src/main/nightwatch.json')); diff --git a/acceptance-tests/runner/runtime/src/test/java/utils/DevRunner.java b/acceptance-tests/runner/runtime/src/test/java/utils/DevRunner.java index 2658ff30851..fde3b070e92 100644 --- a/acceptance-tests/runner/runtime/src/test/java/utils/DevRunner.java +++ b/acceptance-tests/runner/runtime/src/test/java/utils/DevRunner.java @@ -31,7 +31,7 @@ * @author tom.fennelly@gmail.com */ public class DevRunner extends BOJUnitTest { - + @Test public void runAndStayRunning() throws Exception { System.out.println(""); @@ -50,7 +50,7 @@ public void runAndStayRunning() throws Exception { System.out.println(" See http://nightwatchjs.org/"); System.out.println(""); System.out.println(" NOTE:"); - System.out.println(" Selenium and the browser (Firefox) are running in a docker"); + System.out.println(" Selenium and the browser (Chrome) are running in a docker"); System.out.println(" container that also has VNC. This allows you to connect if"); System.out.println(" you'd like to look at the browser while the tests run."); System.out.println(" Simple run:"); @@ -59,7 +59,7 @@ public void runAndStayRunning() throws Exception { System.out.println("------------------------------------------------------------------------------------"); System.out.println(""); System.out.println("ctrl-c to exit..."); - + while(true) { Thread.sleep(1000); } diff --git a/acceptance-tests/runner/scripts/start-selenium.sh b/acceptance-tests/runner/scripts/start-selenium.sh index 082375dff2e..8362cbe0874 100755 --- a/acceptance-tests/runner/scripts/start-selenium.sh +++ b/acceptance-tests/runner/scripts/start-selenium.sh @@ -15,7 +15,7 @@ echo $SELENIUM_IP > ./target/.selenium_ip echo "" echo "**************************************************************" -echo "**** Docker container with Selenium, Firefox and VNC running." +echo "**** Docker container with Selenium, Chrome and VNC running." echo "**** Selenium server listening on $SELENIUM_IP:4444" echo "**** " echo "**** To connect and view with VNC, run:" diff --git a/acceptance-tests/src/main/nightwatch.json b/acceptance-tests/src/main/nightwatch.json index 67aebc3003a..37f5cb542c0 100644 --- a/acceptance-tests/src/main/nightwatch.json +++ b/acceptance-tests/src/main/nightwatch.json @@ -30,7 +30,7 @@ "selenium_port": 4444, "selenium_host": "localhost", "desiredCapabilities": { - "browserName": "firefox", + "browserName": "chrome", "javascriptEnabled": true, "acceptSslCerts": true } From af4c8de9d14d2fb3870b73e7d660ea3a645aec4a Mon Sep 17 00:00:00 2001 From: Cliff Meyers Date: Wed, 4 Oct 2017 13:15:15 -0400 Subject: [PATCH 09/30] update the "deletePipeline" logic to delete by repo name instead of org name - this was broken after GitHub OrgFolder - MBP change --- .../blueocean/ath/pages/blue/GithubCreationPage.java | 10 +++++++--- .../ath/live/GithubEnterpriseCreationTest.java | 2 +- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/acceptance-tests/src/main/java/io/blueocean/ath/pages/blue/GithubCreationPage.java b/acceptance-tests/src/main/java/io/blueocean/ath/pages/blue/GithubCreationPage.java index 47d4be0b7a1..b71b0ae2434 100644 --- a/acceptance-tests/src/main/java/io/blueocean/ath/pages/blue/GithubCreationPage.java +++ b/acceptance-tests/src/main/java/io/blueocean/ath/pages/blue/GithubCreationPage.java @@ -111,12 +111,16 @@ public void createPipeline(String apikey, String org, String pipeline) throws IO createPipeline(apikey, org, pipeline, false); } public void createPipeline(String apiKey, String org, String pipeline, boolean createJenkinsFile) throws IOException { - beginCreationFlow(org); + beginCreationFlow(pipeline); completeCreationFlow(apiKey, org, pipeline, createJenkinsFile); } - public void beginCreationFlow(String org) throws IOException { - jobApi.deletePipeline(org); + /** + * @param jobName name of job to be created + * @throws IOException + */ + public void beginCreationFlow(String jobName) throws IOException { + jobApi.deletePipeline(jobName); navigateToCreation(); selectGithubCreation(); } diff --git a/acceptance-tests/src/test/java/io/blueocean/ath/live/GithubEnterpriseCreationTest.java b/acceptance-tests/src/test/java/io/blueocean/ath/live/GithubEnterpriseCreationTest.java index 563806b2cb9..d28b9ef3b11 100644 --- a/acceptance-tests/src/test/java/io/blueocean/ath/live/GithubEnterpriseCreationTest.java +++ b/acceptance-tests/src/test/java/io/blueocean/ath/live/GithubEnterpriseCreationTest.java @@ -65,7 +65,7 @@ public void testGitHubEnterpriseCreation_addNewGitHubServer() throws IOException String serverName = getServerNameUnique("My Server"); String serverUrl = getServerUrl(mockServer); - creationPage.beginCreationFlow(config.getOrganization()); + creationPage.beginCreationFlow(config.getRepository()); creationPage.clickAddServerButton(); // "empty form" validation From d0f62f06686573fda26b741c084eb81010ccd779 Mon Sep 17 00:00:00 2001 From: Cliff Meyers Date: Wed, 4 Oct 2017 13:30:19 -0400 Subject: [PATCH 10/30] hide editor's Cancel and Save buttons until the pipeline is loaded --- blueocean-pipeline-editor/src/main/js/EditorPage.jsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/blueocean-pipeline-editor/src/main/js/EditorPage.jsx b/blueocean-pipeline-editor/src/main/js/EditorPage.jsx index 3b7a54943e7..fa8bf41354c 100644 --- a/blueocean-pipeline-editor/src/main/js/EditorPage.jsx +++ b/blueocean-pipeline-editor/src/main/js/EditorPage.jsx @@ -560,10 +560,12 @@ class PipelineLoader extends React.Component { {pipeline && title} + {pipelineStore.pipeline &&
{pipelineName && }
+ } {pipelineStore.pipeline &&
From 05d72b961a6c0456e4aa9b42ec050bc97aadff5e Mon Sep 17 00:00:00 2001 From: Cliff Meyers Date: Wed, 4 Oct 2017 15:36:50 -0400 Subject: [PATCH 11/30] just remove the browser name altogether from messaging --- acceptance-tests/nightwatch.conf.js | 2 +- .../runner/runtime/src/test/java/utils/DevRunner.java | 2 +- acceptance-tests/runner/scripts/start-selenium.sh | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/acceptance-tests/nightwatch.conf.js b/acceptance-tests/nightwatch.conf.js index dd99a12a832..d5eee5edba3 100644 --- a/acceptance-tests/nightwatch.conf.js +++ b/acceptance-tests/nightwatch.conf.js @@ -46,7 +46,7 @@ module.exports = (function (settings) { console.log('Jenkins running at: ' + settings.test_settings.default.launch_url); console.log(" NOTE:"); - console.log(" Selenium and the browser (Chrome) are running in a docker"); + console.log(" Selenium and the browser are running in a docker"); console.log(" container that also has VNC. This allows you to connect if"); console.log(" you'd like to look at the browser while the tests run."); console.log(" Simple run:"); diff --git a/acceptance-tests/runner/runtime/src/test/java/utils/DevRunner.java b/acceptance-tests/runner/runtime/src/test/java/utils/DevRunner.java index fde3b070e92..10085777413 100644 --- a/acceptance-tests/runner/runtime/src/test/java/utils/DevRunner.java +++ b/acceptance-tests/runner/runtime/src/test/java/utils/DevRunner.java @@ -50,7 +50,7 @@ public void runAndStayRunning() throws Exception { System.out.println(" See http://nightwatchjs.org/"); System.out.println(""); System.out.println(" NOTE:"); - System.out.println(" Selenium and the browser (Chrome) are running in a docker"); + System.out.println(" Selenium and the browser are running in a docker"); System.out.println(" container that also has VNC. This allows you to connect if"); System.out.println(" you'd like to look at the browser while the tests run."); System.out.println(" Simple run:"); diff --git a/acceptance-tests/runner/scripts/start-selenium.sh b/acceptance-tests/runner/scripts/start-selenium.sh index 8362cbe0874..e70afaf409c 100755 --- a/acceptance-tests/runner/scripts/start-selenium.sh +++ b/acceptance-tests/runner/scripts/start-selenium.sh @@ -15,7 +15,7 @@ echo $SELENIUM_IP > ./target/.selenium_ip echo "" echo "**************************************************************" -echo "**** Docker container with Selenium, Chrome and VNC running." +echo "**** Docker container with Selenium, browser and VNC running." echo "**** Selenium server listening on $SELENIUM_IP:4444" echo "**** " echo "**** To connect and view with VNC, run:" From cede23bddb5291e3dce59d554d00083c1cb02277 Mon Sep 17 00:00:00 2001 From: Cliff Meyers Date: Wed, 4 Oct 2017 15:37:54 -0400 Subject: [PATCH 12/30] try to fix an issue in ATH where pipeline scripts were not properly saved due to a setValue timing issue that shows up in Chrome --- .../classic_jenkins/pipelineConfig.js | 15 ++++++++++----- .../classic_jenkins/pipelineCreate.js | 4 ++-- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/acceptance-tests/src/main/js/page_objects/classic_jenkins/pipelineConfig.js b/acceptance-tests/src/main/js/page_objects/classic_jenkins/pipelineConfig.js index cfe1b7093da..f6d42457888 100644 --- a/acceptance-tests/src/main/js/page_objects/classic_jenkins/pipelineConfig.js +++ b/acceptance-tests/src/main/js/page_objects/classic_jenkins/pipelineConfig.js @@ -10,6 +10,7 @@ var fs = require('fs'); exports.elements = { scriptInput: '#workflow-editor-1 .ace_text-input', + scriptContent: '#workflow-editor-1 .ace_content', configForm: 'form[name="config"]', save: '#newFormSubmitButtonForATH' }; @@ -32,16 +33,20 @@ exports.commands = [ * @returns {Object} self - nightwatch page object */ setPipelineScript: function (script) { + var self = this; var scriptText = readTestScript(script); - + // Need to wait for the ACE Editor to fully render on the page this.waitForElementPresent('@scriptInput'); + // give focus to avoid bug in Chrome when using setValue below + this.click('@scriptContent'); + this.api.execute(function (selector, scriptText) { var targets = document.getElementsBySelector(selector); targets[0].aceEditor.setValue(scriptText); return true; }, ['#workflow-editor-1', scriptText]); - + return this; } } @@ -53,12 +58,12 @@ exports.commands = [ */ function readTestScript(script) { var fileName = 'src/test/resources/test_scripts/' + script; - + if (!fs.existsSync(fileName)) { // It's not a script file. // Must be a raw script text. return script; } - + return fs.readFileSync(fileName, 'utf8'); -} \ No newline at end of file +} diff --git a/acceptance-tests/src/main/js/page_objects/classic_jenkins/pipelineCreate.js b/acceptance-tests/src/main/js/page_objects/classic_jenkins/pipelineCreate.js index b4f7d1e21cb..30a800bbdb2 100644 --- a/acceptance-tests/src/main/js/page_objects/classic_jenkins/pipelineCreate.js +++ b/acceptance-tests/src/main/js/page_objects/classic_jenkins/pipelineCreate.js @@ -24,7 +24,7 @@ module.exports.commands = [{ */ createPipeline: function(jobName, script, oncreated) { var self = this; - + self.waitForJobDeleted(jobName); self.setValue('@nameInput', jobName); @@ -48,4 +48,4 @@ module.exports.commands = [{ .waitForElementPresent('@configForm') .click('@save', oncreated); }, -}]; \ No newline at end of file +}]; From ebcf1b515821932bf7dfe74c815cc85000669958 Mon Sep 17 00:00:00 2001 From: Cliff Meyers Date: Wed, 4 Oct 2017 15:54:08 -0400 Subject: [PATCH 13/30] use the latest Chrome, since the rest of the world will too --- acceptance-tests/runner/scripts/start-selenium.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acceptance-tests/runner/scripts/start-selenium.sh b/acceptance-tests/runner/scripts/start-selenium.sh index e70afaf409c..84380bfd15f 100755 --- a/acceptance-tests/runner/scripts/start-selenium.sh +++ b/acceptance-tests/runner/scripts/start-selenium.sh @@ -6,7 +6,7 @@ $SCRIPT_DIR/stop-selenium.sh echo "" echo " Starting Selenium Docker container..." echo "" -docker run -d --name blueo-selenium -p 15900:5900 -p 4444:4444 -e no_proxy=localhost selenium/standalone-chrome-debug:2.53.0 > /dev/null +docker run -d --name blueo-selenium -p 15900:5900 -p 4444:4444 -e no_proxy=localhost selenium/standalone-chrome-debug:latest > /dev/null # Output the containers bridge network IP to file SELENIUM_IP=`docker inspect -f '{{ .NetworkSettings.IPAddress }}' blueo-selenium` From c9f0bf748b6fb12b810991f1b5f037c68ece4ce4 Mon Sep 17 00:00:00 2001 From: Cliff Meyers Date: Wed, 4 Oct 2017 15:54:23 -0400 Subject: [PATCH 14/30] add some TODOs around areas of test fragility --- acceptance-tests/src/test/js/failingStages.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/acceptance-tests/src/test/js/failingStages.js b/acceptance-tests/src/test/js/failingStages.js index d585eb7b7fc..14211dcd68c 100644 --- a/acceptance-tests/src/test/js/failingStages.js +++ b/acceptance-tests/src/test/js/failingStages.js @@ -78,6 +78,8 @@ module.exports = { //click the re run button blueRunDetailPage.waitForElementVisible('.result-item.failure.expanded'); blueRunDetailPage.clickReRunButton(); + // TODO: this wait fails frequently (in Chrome at least). the stage graph stays visible and is not replaced + // when the data is refetched blueRunDetailPage.waitForElementNotPresent('.result-item.failure.expanded'); //Ccheck that it runs and we could stop if if we want to @@ -90,6 +92,7 @@ module.exports = { blueRunDetailPage.waitForElementVisible('.pipeline-node-selected'); blueRunDetailPage.waitForElementVisible('.download-log-button'); blueRunDetailPage.waitForElementVisible('.pipeline-selection-highlight'); + // TODO: these 2 waits also seem to fail in Chrome blueRunDetailPage.waitForElementVisible('.pipeline-connector'); blueRunDetailPage.waitForElementVisible('.pipeline-node-hittarget'); From 48928b3736133cbd55a00a4ef1693cab2ba334c5 Mon Sep 17 00:00:00 2001 From: Cliff Meyers Date: Thu, 5 Oct 2017 10:55:33 -0400 Subject: [PATCH 15/30] wait for Branches tab to be visible before clicking it --- .../src/test/js/log-karaoke/parametrisedPipeline.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/acceptance-tests/src/test/js/log-karaoke/parametrisedPipeline.js b/acceptance-tests/src/test/js/log-karaoke/parametrisedPipeline.js index 6dfa5292c5e..3cf0449736b 100644 --- a/acceptance-tests/src/test/js/log-karaoke/parametrisedPipeline.js +++ b/acceptance-tests/src/test/js/log-karaoke/parametrisedPipeline.js @@ -33,7 +33,8 @@ module.exports = { multibranchCreate.createBranch(jobName, pathToRepo); const blueActivityPage = browser.page.bluePipelineActivity().forJob(jobName, 'jenkins'); - blueActivityPage.click('.branches'); + blueActivityPage.waitForElementVisible('.Header-pageTabs .branches'); + blueActivityPage.click('.Header-pageTabs .branches'); blueActivityPage.waitForElementVisible('.JTable-row[data-branch="master"]'); blueActivityPage.waitForElementVisible('a.run-button'); blueActivityPage.click('a.run-button'); From f03aabf36c2f485ae4182f0f600e1faf8b1a60a5 Mon Sep 17 00:00:00 2001 From: Cliff Meyers Date: Thu, 5 Oct 2017 11:15:36 -0400 Subject: [PATCH 16/30] asserting the location immediately could fail since a redirect needs to happen first; using "waitForLocationContains" seems more robust --- acceptance-tests/src/test/js/multibranch/folder.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/acceptance-tests/src/test/js/multibranch/folder.js b/acceptance-tests/src/test/js/multibranch/folder.js index d0b21b79e79..6217889de0b 100644 --- a/acceptance-tests/src/test/js/multibranch/folder.js +++ b/acceptance-tests/src/test/js/multibranch/folder.js @@ -61,13 +61,14 @@ module.exports = { */ 'step 03 - Open Blue Ocean (from a run details)': function(browser) { var classicRunPage = browser.page.classicRun(); + var runDetailsPage = browser.page.bluePipelineRunDetail(); classicRunPage.navigateToRun('aFolder/job/bFolder/job/cFolder/job/MBPInFolderTree/job/master'); // make sure the open blue ocean button works. In this case, // it should bring the browser to the run details page for the first run. browser.page.openBlueOcean().open(); - browser.assert.urlEndsWith('/blue/organizations/jenkins/aFolder%2FbFolder%2FcFolder%2FMBPInFolderTree/detail/master/1/pipeline'); + runDetailsPage.waitForLocationContains('/blue/organizations/jenkins/aFolder%2FbFolder%2FcFolder%2FMBPInFolderTree/detail/master/1/pipeline'); browser.url(function (response) { sanityCheck(browser, response); From 00934a36b24975f30f370e1d73a36cb90ffee6a6 Mon Sep 17 00:00:00 2001 From: Cliff Meyers Date: Thu, 5 Oct 2017 11:51:03 -0400 Subject: [PATCH 17/30] wait for Branches tab to be visible before clicking it --- .../test/js/multibranch/multibranchOpening.js | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/acceptance-tests/src/test/js/multibranch/multibranchOpening.js b/acceptance-tests/src/test/js/multibranch/multibranchOpening.js index ecd234cac84..0c67e9b3462 100644 --- a/acceptance-tests/src/test/js/multibranch/multibranchOpening.js +++ b/acceptance-tests/src/test/js/multibranch/multibranchOpening.js @@ -10,7 +10,7 @@ function rowSelectorFor(jobName) { /** @module multibranchOpening * @memberof multibranch - * @description Check we run and open results screen for multibranch projects, + * @description Check we run and open results screen for multibranch projects, * and that the stage graph shows and completes. */ module.exports = { @@ -53,14 +53,14 @@ module.exports = { var multibranchCreate = browser.page.multibranchCreate().navigate(); multibranchCreate.createBranch(jobName, pathToRepo); - var blueActivityPage = browser.page.bluePipelineActivity().forJob(jobName, 'jenkins'); - blueActivityPage.click(".branches"); - - blueActivityPage.waitForElementVisible('.JTable-row[data-branch="master"]'); - blueActivityPage.click('.JTable-row[data-branch="master"]'); + var blueActivityPage = browser.page.bluePipelineActivity().forJob(jobName, 'jenkins'); + blueActivityPage.waitForElementVisible('.Header-pageTabs .branches'); + blueActivityPage.click(".Header-pageTabs .branches"); - blueActivityPage.assertStageGraphShows(); + blueActivityPage.waitForElementVisible('.JTable-row[data-branch="master"]'); + blueActivityPage.click('.JTable-row[data-branch="master"]'); + blueActivityPage.assertStageGraphShows(); }, /** @@ -82,18 +82,18 @@ module.exports = { blueActivityPage.assertStageGraphShows(); }, - + /** * Make sure we can open the feature/1 branch from branch screen - * Regression: https://issues.jenkins-ci.org/browse/JENKINS-40027 + * Regression: https://issues.jenkins-ci.org/browse/JENKINS-40027 */ 'open feature/1 from branches tab': function (browser) { - - var jobName = "featureBranchesMB"; - var multibranchCreate = browser.page.multibranchCreate().navigate(); + + var jobName = "featureBranchesMB"; + var multibranchCreate = browser.page.multibranchCreate().navigate(); multibranchCreate.createBranch(jobName, pathToRepo); - + var blueActivityPage = browser.page.bluePipelineActivity().forJob(jobName, 'jenkins'); blueActivityPage.waitForElementVisible('.branches'); blueActivityPage.click(".branches"); @@ -103,7 +103,7 @@ module.exports = { blueActivityPage.click(rowSelector); blueActivityPage.assertStageGraphShows(); - + } } From 9e30db691e2d4f69a6cc974416fb5810df677ec3 Mon Sep 17 00:00:00 2001 From: Cliff Meyers Date: Thu, 5 Oct 2017 11:59:39 -0400 Subject: [PATCH 18/30] be more specific about which "replay" button to click --- .../main/js/page_objects/blueocean/bluePipelineRunDetail.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/acceptance-tests/src/main/js/page_objects/blueocean/bluePipelineRunDetail.js b/acceptance-tests/src/main/js/page_objects/blueocean/bluePipelineRunDetail.js index 9ce1b49eb23..39c3224071c 100644 --- a/acceptance-tests/src/main/js/page_objects/blueocean/bluePipelineRunDetail.js +++ b/acceptance-tests/src/main/js/page_objects/blueocean/bluePipelineRunDetail.js @@ -434,9 +434,8 @@ module.exports.commands = [{ */ clickReRunButton: function () { var self = this; - const browser = this.api; - self.waitForElementVisible('.replay-button'); - self.click('.replay-button'); + self.waitForElementVisible('.ResultPageHeader-run .replay-button'); + self.click('.ResultPageHeader-run .replay-button'); return self; } From b1139706767bff276a1bae1fd7c90bca6eacdb2e Mon Sep 17 00:00:00 2001 From: Cliff Meyers Date: Thu, 5 Oct 2017 11:59:58 -0400 Subject: [PATCH 19/30] add another note about a flaky assertion / test --- acceptance-tests/src/test/js/failing.js | 1 + 1 file changed, 1 insertion(+) diff --git a/acceptance-tests/src/test/js/failing.js b/acceptance-tests/src/test/js/failing.js index c7537266fc8..ea0db94a8cd 100644 --- a/acceptance-tests/src/test/js/failing.js +++ b/acceptance-tests/src/test/js/failing.js @@ -87,6 +87,7 @@ module.exports = { blueRunDetailPage.waitForElementPresent('.stop-button'); // this will show up when it has finished replaying + // TODO: this assertion is flaky since sometimes run details header state doesn't transition from running-> failed blueRunDetailPage.waitForElementVisible('.replay-button'); } From 6c5aec284e6c585043121fd57b2bf0042a85232e Mon Sep 17 00:00:00 2001 From: Cliff Meyers Date: Mon, 9 Oct 2017 14:08:35 -0400 Subject: [PATCH 20/30] more tweaks to get nightwatch tests to cooperate --- acceptance-tests/src/test/js/failingStages.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/acceptance-tests/src/test/js/failingStages.js b/acceptance-tests/src/test/js/failingStages.js index 14211dcd68c..f95661ae054 100644 --- a/acceptance-tests/src/test/js/failingStages.js +++ b/acceptance-tests/src/test/js/failingStages.js @@ -78,8 +78,8 @@ module.exports = { //click the re run button blueRunDetailPage.waitForElementVisible('.result-item.failure.expanded'); blueRunDetailPage.clickReRunButton(); - // TODO: this wait fails frequently (in Chrome at least). the stage graph stays visible and is not replaced - // when the data is refetched + // once the new run starts, the stage graph should disappear and then be re-rendered + // TODO: this wait fails frequently (in Chrome at least). the stage graph is not updated after new run starts blueRunDetailPage.waitForElementNotPresent('.result-item.failure.expanded'); //Ccheck that it runs and we could stop if if we want to @@ -92,9 +92,9 @@ module.exports = { blueRunDetailPage.waitForElementVisible('.pipeline-node-selected'); blueRunDetailPage.waitForElementVisible('.download-log-button'); blueRunDetailPage.waitForElementVisible('.pipeline-selection-highlight'); - // TODO: these 2 waits also seem to fail in Chrome - blueRunDetailPage.waitForElementVisible('.pipeline-connector'); - blueRunDetailPage.waitForElementVisible('.pipeline-node-hittarget'); + // in Chrome, 'Visible' seems to fail but 'Present' works ok + blueRunDetailPage.waitForElementPresent('.pipeline-connector'); + blueRunDetailPage.waitForElementPresent('.pipeline-node-hittarget'); // this will show up when it has finished replaying blueRunDetailPage.waitForElementVisible('.replay-button'); From e620e6314e9d25a530e6cfdab72baceffb8706fb Mon Sep 17 00:00:00 2001 From: Cliff Meyers Date: Mon, 9 Oct 2017 14:15:27 -0400 Subject: [PATCH 21/30] more tweaks to get nightwatch tests to cooperate --- .../blueocean/bluePipelineActivity.js | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/acceptance-tests/src/main/js/page_objects/blueocean/bluePipelineActivity.js b/acceptance-tests/src/main/js/page_objects/blueocean/bluePipelineActivity.js index 239c1a20c5f..f6aae64063a 100644 --- a/acceptance-tests/src/main/js/page_objects/blueocean/bluePipelineActivity.js +++ b/acceptance-tests/src/main/js/page_objects/blueocean/bluePipelineActivity.js @@ -97,7 +97,7 @@ module.exports.commands = [{ this.waitForRunVisible(pipeline, runId); const resultRowSelector = activityRowSelector(pipeline, runId); this.waitForElementVisible(`${resultRowSelector} .failure`); - }, + }, /** * Wait for a specific run to appear in the activity table as unstable @@ -133,19 +133,19 @@ module.exports.commands = [{ * Inspect that result screen runs, shows a stage graph, and completes. */ assertStageGraphShows: function() { - //check results look kosher: - this.waitForElementVisible('.progress-spinner.running'); - this.waitForElementVisible('.BasicHeader--running'); - - this.waitForElementVisible('.pipeline-node-selected'); - this.waitForElementVisible('.download-log-button'); - this.waitForElementVisible('.pipeline-selection-highlight'); - this.waitForElementVisible('.pipeline-connector'); - this.waitForElementVisible('.pipeline-node-hittarget'); - this.waitForElementVisible('.BasicHeader--success'); + //check results look kosher: + this.waitForElementVisible('.progress-spinner.running'); + this.waitForElementVisible('.BasicHeader--running'); + this.waitForElementVisible('.pipeline-node-selected'); + this.waitForElementVisible('.download-log-button'); + this.waitForElementVisible('.pipeline-selection-highlight'); + // in Chrome, 'Visible' seems to fail but 'Present' works ok + this.waitForElementPresent('.pipeline-connector'); + this.waitForElementPresent('.pipeline-node-hittarget'); + this.waitForElementVisible('.BasicHeader--success'); }, - + /** * Click css selector of a specific tab * @param tab {string} the tab we want to select From 17cc48ab550b69e7d976ff7e29d8955d0f38fb2a Mon Sep 17 00:00:00 2001 From: Cliff Meyers Date: Mon, 9 Oct 2017 15:18:05 -0400 Subject: [PATCH 22/30] hide warning about password field on non-https page --- .../src/main/java/io/blueocean/ath/LogEntryLogger.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/acceptance-tests/src/main/java/io/blueocean/ath/LogEntryLogger.java b/acceptance-tests/src/main/java/io/blueocean/ath/LogEntryLogger.java index 2bde72dd580..ba0b7388551 100644 --- a/acceptance-tests/src/main/java/io/blueocean/ath/LogEntryLogger.java +++ b/acceptance-tests/src/main/java/io/blueocean/ath/LogEntryLogger.java @@ -47,9 +47,11 @@ static void recordLogEntry(LogEntry entry) { // special handling to suppress some repetitive logging messages that are not helpful private static final String MESSAGE_JS_LOGGING = "@jenkins-cd/logging is explained"; private static final String MESSAGE_CHROME_CONSOLE = "Chrome displays console errors"; + private static final String MESSAGE_PASSWORD_INSECURE = "page includes a password or credit card input"; static boolean isSuperfluousLogEntry(LogEntry entry) { String message = entry.getMessage(); - return message.contains(MESSAGE_JS_LOGGING) || message.contains(MESSAGE_CHROME_CONSOLE); + return message.contains(MESSAGE_JS_LOGGING) || message.contains(MESSAGE_CHROME_CONSOLE) || + message.contains(MESSAGE_PASSWORD_INSECURE); } } From 34faca8d927b2613c6bb322f49a32e968c41d527 Mon Sep 17 00:00:00 2001 From: Cliff Meyers Date: Mon, 9 Oct 2017 15:39:57 -0400 Subject: [PATCH 23/30] try to address an error in CI about span.IconButton-text not being clickable. click the button element instead --- .../main/java/io/blueocean/ath/pages/blue/GitCreationPage.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acceptance-tests/src/main/java/io/blueocean/ath/pages/blue/GitCreationPage.java b/acceptance-tests/src/main/java/io/blueocean/ath/pages/blue/GitCreationPage.java index 92cfc3e4a94..030267b4dda 100644 --- a/acceptance-tests/src/main/java/io/blueocean/ath/pages/blue/GitCreationPage.java +++ b/acceptance-tests/src/main/java/io/blueocean/ath/pages/blue/GitCreationPage.java @@ -42,7 +42,7 @@ public GitCreationPage(WebDriver driver) { } public GitCreationPage clickGitCreationOption() { - wait.until(By.xpath("//span[text()='Git']")).click(); + wait.until(By.cssSelector(".scm-provider-list .git-creation")).click(); logger.info("Selected git creation"); return this; } From 21f7d43e5a52d15ba3f2b2f4b84d580552f73e42 Mon Sep 17 00:00:00 2001 From: Cliff Meyers Date: Mon, 9 Oct 2017 16:51:24 -0400 Subject: [PATCH 24/30] remove unused obsolete element corresponding to the old "single repo" button from GitHub flow --- .../java/io/blueocean/ath/pages/blue/GithubCreationPage.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/acceptance-tests/src/main/java/io/blueocean/ath/pages/blue/GithubCreationPage.java b/acceptance-tests/src/main/java/io/blueocean/ath/pages/blue/GithubCreationPage.java index 606651dd6c5..33e5c960110 100644 --- a/acceptance-tests/src/main/java/io/blueocean/ath/pages/blue/GithubCreationPage.java +++ b/acceptance-tests/src/main/java/io/blueocean/ath/pages/blue/GithubCreationPage.java @@ -5,7 +5,6 @@ import io.blueocean.ath.api.classic.ClassicJobApi; import org.apache.log4j.Logger; import org.openqa.selenium.By; -import org.openqa.selenium.TimeoutException; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; import org.openqa.selenium.support.FindBy; @@ -35,9 +34,6 @@ public GithubCreationPage(WebDriver driver) { @FindBy(css = ".button-connect") public WebElement connectButton; - @FindBy(css = ".button-single-repo") - public WebElement singlePipelineBtn; - @FindBy(css = ".repo-list input") public WebElement pipelineSearchInput; From 20782ff483f5af51c35d00b3578edfd2064c3c30 Mon Sep 17 00:00:00 2001 From: Cliff Meyers Date: Mon, 9 Oct 2017 16:52:45 -0400 Subject: [PATCH 25/30] try to fix an apparent timing issue where "create pipeline" would be clicked before it transitioned from disabled -> enabled --- .../io/blueocean/ath/pages/blue/GithubCreationPage.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/acceptance-tests/src/main/java/io/blueocean/ath/pages/blue/GithubCreationPage.java b/acceptance-tests/src/main/java/io/blueocean/ath/pages/blue/GithubCreationPage.java index 33e5c960110..26484a3c0fa 100644 --- a/acceptance-tests/src/main/java/io/blueocean/ath/pages/blue/GithubCreationPage.java +++ b/acceptance-tests/src/main/java/io/blueocean/ath/pages/blue/GithubCreationPage.java @@ -38,7 +38,7 @@ public GithubCreationPage(WebDriver driver) { public WebElement pipelineSearchInput; @FindBy(css = ".button-create") - public WebElement createBtn; + public WebElement createPipelineButton; @Inject @BaseUrl @@ -107,6 +107,10 @@ public void selectPipelineToCreate(String pipeline){ logger.info("Selected pipeline to create"); } + public void clickCreatePipelineButton() { + wait.until(ExpectedConditions.elementToBeClickable(createPipelineButton)).click(); + } + public By emptyRepositoryCreateButton = By.cssSelector(".jenkins-pipeline-create-missing-jenkinsfile > div > button"); public void createPipeline(String apikey, String org, String pipeline) throws IOException { @@ -139,8 +143,7 @@ public void completeCreationFlow(String apiKey, String org, String pipeline, boo logger.info("Select a repo to create"); selectPipelineToCreate(pipeline); - - wait.until(createBtn).click(); + clickCreatePipelineButton(); if(createJenkinsFile) { WebElement createJenkinsFileButton = wait From ea4e0c6d84383f83d85c27e4112da126d639e9a8 Mon Sep 17 00:00:00 2001 From: Cliff Meyers Date: Mon, 9 Oct 2017 18:13:05 -0400 Subject: [PATCH 26/30] use string concat for logging unhandled errors since selenium likes to ignore console.log for complex objects --- blueocean-web/src/main/js/ErrorUtils.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/blueocean-web/src/main/js/ErrorUtils.js b/blueocean-web/src/main/js/ErrorUtils.js index 470ee224816..1920b822cb1 100644 --- a/blueocean-web/src/main/js/ErrorUtils.js +++ b/blueocean-web/src/main/js/ErrorUtils.js @@ -31,7 +31,7 @@ function logApplicationError(messageOrEvent) { message = messageOrEvent; } - console.error('Unhandled Error: ', JSON.stringify(message, null, 4)); + console.error('Unhandled Error: ' + JSON.stringify(message, null, 4)); if (messageOrEvent.preventDefault) { messageOrEvent.preventDefault(); @@ -49,7 +49,7 @@ function logUnhandledPromiseRejection(errorEvent) { message = errorEvent; } - console.error('Unhandled Rejection: ', JSON.stringify(message, null, 4)); + console.error('Unhandled Rejection: ' + JSON.stringify(message, null, 4)); if (errorEvent.preventDefault) { errorEvent.preventDefault(); From f296d07e9422647f8d5e368eb7f36347fa648f0a Mon Sep 17 00:00:00 2001 From: Cliff Meyers Date: Wed, 11 Oct 2017 17:21:26 -0400 Subject: [PATCH 27/30] lock version of docker chrome image --- acceptance-tests/runner/scripts/start-selenium.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acceptance-tests/runner/scripts/start-selenium.sh b/acceptance-tests/runner/scripts/start-selenium.sh index 84380bfd15f..7d38f08ee9d 100755 --- a/acceptance-tests/runner/scripts/start-selenium.sh +++ b/acceptance-tests/runner/scripts/start-selenium.sh @@ -6,7 +6,7 @@ $SCRIPT_DIR/stop-selenium.sh echo "" echo " Starting Selenium Docker container..." echo "" -docker run -d --name blueo-selenium -p 15900:5900 -p 4444:4444 -e no_proxy=localhost selenium/standalone-chrome-debug:latest > /dev/null +docker run -d --name blueo-selenium -p 15900:5900 -p 4444:4444 -e no_proxy=localhost selenium/standalone-chrome-debug:3.6.0-bromine > /dev/null # Output the containers bridge network IP to file SELENIUM_IP=`docker inspect -f '{{ .NetworkSettings.IPAddress }}' blueo-selenium` From 5226edcacd656a1b734a501e3a35b3256584d8e4 Mon Sep 17 00:00:00 2001 From: Cliff Meyers Date: Thu, 12 Oct 2017 10:29:40 -0400 Subject: [PATCH 28/30] introduce "click" utility methods that wait for element to be clickable, then click. if we can repro the "can't click on element" error, then this is a good place for retry logic --- .../src/main/java/io/blueocean/ath/WaitUtil.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/acceptance-tests/src/main/java/io/blueocean/ath/WaitUtil.java b/acceptance-tests/src/main/java/io/blueocean/ath/WaitUtil.java index 72fdda66be5..6d87a8183b4 100644 --- a/acceptance-tests/src/main/java/io/blueocean/ath/WaitUtil.java +++ b/acceptance-tests/src/main/java/io/blueocean/ath/WaitUtil.java @@ -82,4 +82,12 @@ public Function orVisible(Function Date: Thu, 12 Oct 2017 10:30:14 -0400 Subject: [PATCH 29/30] use an anchor instead of span for sheet's back button with the hope it will be a more reliable click target for Selenium --- .../src/main/java/io/blueocean/ath/pages/blue/EditorPage.java | 2 +- blueocean-pipeline-editor/src/main/js/components/Sheets.jsx | 4 ++-- blueocean-pipeline-editor/src/main/less/sheets.less | 1 + 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/acceptance-tests/src/main/java/io/blueocean/ath/pages/blue/EditorPage.java b/acceptance-tests/src/main/java/io/blueocean/ath/pages/blue/EditorPage.java index 7acbfc9df53..084704542db 100644 --- a/acceptance-tests/src/main/java/io/blueocean/ath/pages/blue/EditorPage.java +++ b/acceptance-tests/src/main/java/io/blueocean/ath/pages/blue/EditorPage.java @@ -55,7 +55,7 @@ public void simplePipeline(String newBranch) { wait.until(By.cssSelector("button.btn-primary.add")).click(); wait.until(By.xpath("//*[text()='Print Message']")).click(); wait.until(By.cssSelector("input.TextInput-control")).sendKeys("hi there"); - wait.until(By.xpath("(//span[@class='back-from-sheet'])[2]")).click(); + wait.click(By.xpath("(//a[@class='back-from-sheet'])[2]")); wait.until(By.xpath("//*[text()='Save']")).click(); wait.until(By.cssSelector("textarea[placeholder=\"What changed?\"]")).sendKeys("Simple pipeline"); if(!Strings.isNullOrEmpty(newBranch)) { diff --git a/blueocean-pipeline-editor/src/main/js/components/Sheets.jsx b/blueocean-pipeline-editor/src/main/js/components/Sheets.jsx index 30c116aa1a6..ea073c67130 100644 --- a/blueocean-pipeline-editor/src/main/js/components/Sheets.jsx +++ b/blueocean-pipeline-editor/src/main/js/components/Sheets.jsx @@ -12,9 +12,9 @@ export class Sheet extends React.Component { return (
{child.props.onClose && - this.onClose()}> + this.onClose()}> - + } {child.getTitle && child.getTitle() || child.props.title}
diff --git a/blueocean-pipeline-editor/src/main/less/sheets.less b/blueocean-pipeline-editor/src/main/less/sheets.less index 914e1a9ad20..cdefdb21d05 100644 --- a/blueocean-pipeline-editor/src/main/less/sheets.less +++ b/blueocean-pipeline-editor/src/main/less/sheets.less @@ -54,6 +54,7 @@ cursor: pointer; svg { + display: block; fill: #c3cfd7; } From fe13d066046f486aa8c348c9c9629c3b75ceef88 Mon Sep 17 00:00:00 2001 From: Cliff Meyers Date: Thu, 12 Oct 2017 11:39:15 -0400 Subject: [PATCH 30/30] implement retry logic for click --- .../main/java/io/blueocean/ath/WaitUtil.java | 29 +++++++++++++++---- 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/acceptance-tests/src/main/java/io/blueocean/ath/WaitUtil.java b/acceptance-tests/src/main/java/io/blueocean/ath/WaitUtil.java index 6d87a8183b4..aedea52d9cb 100644 --- a/acceptance-tests/src/main/java/io/blueocean/ath/WaitUtil.java +++ b/acceptance-tests/src/main/java/io/blueocean/ath/WaitUtil.java @@ -6,6 +6,7 @@ import org.openqa.selenium.NoSuchElementException; import org.openqa.selenium.NotFoundException; import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebDriverException; import org.openqa.selenium.WebElement; import org.openqa.selenium.support.ui.ExpectedConditions; import org.openqa.selenium.support.ui.FluentWait; @@ -18,7 +19,7 @@ @Singleton public class WaitUtil { private Logger logger = Logger.getLogger(WaitUtil.class); - + private static final int RETRY_COUNT = 2; private WebDriver driver; @@ -82,12 +83,28 @@ public Function orVisible(Function 0) { + logger.info(String.format("retry click successful for %s", by.toString())); + } + return; + } catch (WebDriverException ex) { + if (ex.getMessage().contains("is not clickable at point")) { + logger.warn(String.format("%s not clickable: will retry click", by.toString())); + logger.debug("exception: " + ex.getMessage()); + } else { + throw ex; + } + } + } } }