From 3b8d180ef05829148fb0d72614060daf18881d33 Mon Sep 17 00:00:00 2001 From: Chanatan Charnkijtawarush Date: Tue, 24 Mar 2020 02:01:13 -0700 Subject: [PATCH] Add feature to start or stop video recording using cookie (#1085) * Add feature to start or stop video recording using cookie Per #490, video recording can be started, if disabled, or stopped, if enabled, by sending cookie named 'zaleniumVideo'. This is beneficial when multiple tests run in the same session. Additionally to assist with identifying tests in the dashboard, a cookie named 'zaleniumTestName' can be passed in to set the test name. This name will appear in the dashboard. If name already exists, a count will be appended to the name. * Making the test more complete * Add feature to start or stop video recording using cookie Per #490, video recording can be started, if disabled, or stopped, if enabled, by sending cookie named 'zaleniumVideo'. This is beneficial when multiple tests run in the same session. Additionally to assist with identifying tests in the dashboard, a cookie named 'zaleniumTestName' can be passed in to set the test name. This name will appear in the dashboard. If name already exists, a count will be appended to the name. * Adding TestNG groups Co-authored-by: Chanatan Charnkijtawarush Co-authored-by: Diego Molina --- charts/zalenium/values.yaml | 4 +- docs/_posts/2000-01-08-usage.md | 36 ++++++++++ run_integration_tests.sh | 12 ++-- .../zalenium/dashboard/TestInformation.java | 11 +++ .../proxy/DockerSeleniumRemoteProxy.java | 61 ++++++++++++---- .../de/zalando/ep/zalenium/it/ParallelIT.java | 54 +++++++++++++- .../proxy/DockerSeleniumRemoteProxyTest.java | 70 +++++++++++++++++++ 7 files changed, 225 insertions(+), 23 deletions(-) diff --git a/charts/zalenium/values.yaml b/charts/zalenium/values.yaml index e9ebce3149..6f0771eb78 100644 --- a/charts/zalenium/values.yaml +++ b/charts/zalenium/values.yaml @@ -5,7 +5,7 @@ hub: ## The tag for the image ## ref: https://hub.docker.com/r/dosel/zalenium/tags - tag: "3" + tag: "latest" ## Specify a imagePullPolicy ## ref: http://kubernetes.io/docs/user-guide/images/#pre-pulling-images @@ -250,4 +250,4 @@ nodeSelector: hostAliases: [] # - ip: "127.0.0.1" # hostnames: -# - "cosbench.local" \ No newline at end of file +# - "cosbench.local" diff --git a/docs/_posts/2000-01-08-usage.md b/docs/_posts/2000-01-08-usage.md index 41be671f86..a01f14b4a4 100644 --- a/docs/_posts/2000-01-08-usage.md +++ b/docs/_posts/2000-01-08-usage.md @@ -331,6 +331,42 @@ Thanks [adrichem](https://github.com/adrichem) for implementing this feature! Mo +#### Change video recording status while in session +
+ Click for details. + +
+ It is possible to enable or disable video recording while the test is running regardless of what is setup when the session starts. + This is done by sending a cookie with the name zaleniumVideo with a value of true (to enable recording) + or false (to disable recording). Disabling recording while it is originally enable will result in a new video file. This can be useful + when you have multiple tests in one session and want to split up each test in its own file. This feature is only available with elgalu/selenium. + Here is an example in Java: + +{% highlight java %} + Cookie cookie = new Cookie("zaleniumVideo", "true"); + webDriver.manage().addCookie(cookie); +{% endhighlight %} + +
+ +
+ +#### Change test file name while in session +
+ Click for details. + +
+ A video file name can be changed in session by sending a cookie with the name zaleniumTestName with any value. + This can be useful when coupling with zaleniumVideo to give each split video a custom name. Here is an example in Java: + +{% highlight java %} + Cookie cookie = new Cookie("zaleniumTestName", "anyName"); + webDriver.manage().addCookie(cookie); +{% endhighlight %} + +
+ +
*** diff --git a/run_integration_tests.sh b/run_integration_tests.sh index f112ac8388..e9e3e17b58 100755 --- a/run_integration_tests.sh +++ b/run_integration_tests.sh @@ -15,7 +15,7 @@ else if [ "${INTEGRATION_TO_TEST}" = sauceLabs ]; then if [ -n "${SAUCE_USERNAME}" ]; then env "PATH=$PATH" mvn clean - mvn clean verify -Pintegration-test -DthreadCountProperty=2 -Dskip.surefire.tests=true -DintegrationToTest=${INTEGRATION_TO_TEST} + mvn clean verify -Pintegration-test -DthreadCountProperty=2 -Dskip.surefire.tests=true -Dgroups=normal -DintegrationToTest=${INTEGRATION_TO_TEST} # Check for generated videos ls -la ${VIDEOS_FOLDER}/saucelabs*.mp4 || (echo "No Sauce Labs videos were downloaded." && exit 2) ls -la ${VIDEOS_FOLDER}/zalenium*.mp4 || (echo "No Zalenium videos were generated." && exit 2) @@ -24,7 +24,7 @@ else if [ "${INTEGRATION_TO_TEST}" = browserStack ]; then if [ -n "${BROWSER_STACK_USER}" ]; then env "PATH=$PATH" mvn clean - mvn clean verify -Pintegration-test -DthreadCountProperty=2 -Dskip.surefire.tests=true -DintegrationToTest=${INTEGRATION_TO_TEST} + mvn clean verify -Pintegration-test -DthreadCountProperty=2 -Dskip.surefire.tests=true -Dgroups=normal -DintegrationToTest=${INTEGRATION_TO_TEST} # Check for generated videos ls -la ${VIDEOS_FOLDER}/browserstack*.mp4 || (echo "No BrowserStack videos were downloaded." && exit 2) ls -la ${VIDEOS_FOLDER}/zalenium*.mp4 || (echo "No Zalenium videos were generated." && exit 2) @@ -33,7 +33,7 @@ else if [ "${INTEGRATION_TO_TEST}" = testingBot ]; then if [ -n "${TESTINGBOT_KEY}" ]; then env "PATH=$PATH" mvn clean - mvn clean verify -Pintegration-test -DthreadCountProperty=2 -Dskip.surefire.tests=true -DintegrationToTest=${INTEGRATION_TO_TEST} + mvn clean verify -Pintegration-test -DthreadCountProperty=2 -Dskip.surefire.tests=true -Dgroups=normal -DintegrationToTest=${INTEGRATION_TO_TEST} # Check for generated videos ls -la ${VIDEOS_FOLDER}/testingbot*.mp4 || (echo "No TestingBot videos were downloaded." && exit 2) ls -la ${VIDEOS_FOLDER}/zalenium*.mp4 || (echo "No Zalenium videos were generated." && exit 2) @@ -42,7 +42,7 @@ else if [ "${INTEGRATION_TO_TEST}" = crossBrowserTesting ]; then if [ -n "${CBT_USERNAME}" ]; then env "PATH=$PATH" mvn clean - mvn clean verify -Pintegration-test -DthreadCountProperty=2 -Dskip.surefire.tests=true -DintegrationToTest=${INTEGRATION_TO_TEST} + mvn clean verify -Pintegration-test -DthreadCountProperty=2 -Dskip.surefire.tests=true -Dgroups=normal -DintegrationToTest=${INTEGRATION_TO_TEST} # Check for generated videos ls -la ${VIDEOS_FOLDER}/crossbrowsertesting*.mp4 || (echo "No CBT videos were downloaded." && exit 2) ls -la ${VIDEOS_FOLDER}/zalenium*.mp4 || (echo "No Zalenium videos were generated." && exit 2) @@ -51,7 +51,7 @@ else if [ "${INTEGRATION_TO_TEST}" = lambdaTest ]; then if [ -n "${LT_USERNAME}" ]; then env "PATH=$PATH" mvn clean - mvn clean verify -Pintegration-test -DthreadCountProperty=2 -Dskip.surefire.tests=true -DintegrationToTest=${INTEGRATION_TO_TEST} + mvn clean verify -Pintegration-test -DthreadCountProperty=2 -Dskip.surefire.tests=true -Dgroups=normal -DintegrationToTest=${INTEGRATION_TO_TEST} # Check for generated videos ls -la ${VIDEOS_FOLDER}/lambdatest*.mp4 || (echo "No LambdaTest videos were downloaded." && exit 2) ls -la ${VIDEOS_FOLDER}/zalenium*.mp4 || (echo "No Zalenium videos were generated." && exit 2) @@ -64,7 +64,7 @@ else mkdir -p "${VIDEOS_FOLDER}" chmod +x target/zalenium_in_docker_compose.sh target/zalenium_in_docker_compose.sh start - mvn verify -Pintegration-test -DthreadCountProperty=2 -Dskip.surefire.tests=true -Dskip.failsafe.setup=true -DintegrationToTest=sauceLabs + mvn verify -Pintegration-test -DthreadCountProperty=2 -Dskip.surefire.tests=true -Dskip.failsafe.setup=true -Dgroups=normal -DintegrationToTest=sauceLabs # Check for generated videos ls -la ${VIDEOS_FOLDER}/saucelabs*.mp4 || (echo "No Sauce Labs videos were downloaded." && exit 2) ls -la ${VIDEOS_FOLDER}/zalenium*.mp4 || (echo "No Zalenium videos were generated." && exit 2) diff --git a/src/main/java/de/zalando/ep/zalenium/dashboard/TestInformation.java b/src/main/java/de/zalando/ep/zalenium/dashboard/TestInformation.java index c8ccc8f0e1..10a9e86942 100644 --- a/src/main/java/de/zalando/ep/zalenium/dashboard/TestInformation.java +++ b/src/main/java/de/zalando/ep/zalenium/dashboard/TestInformation.java @@ -34,6 +34,7 @@ public class TestInformation { private String platform; private String platformVersion; private String fileName; + private int fileCount = 0; private String fileExtension; private String videoUrl; private List logUrls; @@ -113,6 +114,10 @@ public String getFileName() { return fileName; } + public int getFileCount() { + return fileCount; + } + public List getLogUrls() { return logUrls; } @@ -244,6 +249,12 @@ public String getBrowserAndPlatform() { public JsonObject getMetadata() { return this.metadata;} public void setMetadata(JsonObject metadata) { this.metadata = metadata;} + public void setTestName(String name) { this.testName = name;} + + public void setFileCount(int fileCount) { + this.fileCount = fileCount; + } + public boolean equals(Object obj) { if (obj == null) return false; if (obj == this) return true; diff --git a/src/main/java/de/zalando/ep/zalenium/proxy/DockerSeleniumRemoteProxy.java b/src/main/java/de/zalando/ep/zalenium/proxy/DockerSeleniumRemoteProxy.java index b5b461a745..711367b7f1 100644 --- a/src/main/java/de/zalando/ep/zalenium/proxy/DockerSeleniumRemoteProxy.java +++ b/src/main/java/de/zalando/ep/zalenium/proxy/DockerSeleniumRemoteProxy.java @@ -106,6 +106,7 @@ public class DockerSeleniumRemoteProxy extends DefaultRemoteProxy { private long cleanupStartedTime = 0; private AtomicBoolean timedOut = new AtomicBoolean(false); private long timeRegistered = System.currentTimeMillis(); + private boolean stopRecordingByCookie = false; public DockerSeleniumRemoteProxy(RegistrationRequest request, GridRegistry registry) { super(request, registry); @@ -326,6 +327,27 @@ public void beforeCommand(TestSession session, HttpServletRequest request, HttpS processContainerAction(DockerSeleniumContainerAction.SEND_NOTIFICATION, messageCommand, getContainerId()); } + if ("zaleniumVideo".equalsIgnoreCase(cookieName)) { + boolean recordVideo = Boolean.parseBoolean(cookie.get("value").getAsString()); + if (recordVideo && !isVideoRecordingEnabled()) { + setVideoRecordingEnabledSession(true); + testInformation.setVideoRecorded(true); + stopRecordingByCookie = false; + videoRecording(DockerSeleniumContainerAction.START_RECORDING); + } else if (!recordVideo && isVideoRecordingEnabled()){ + stopRecordingByCookie = true; + videoRecording(DockerSeleniumContainerAction.STOP_RECORDING); + setVideoRecordingEnabledSession(false); + testInformation.setVideoRecorded(false); + } + } + if ("zaleniumTestName".equalsIgnoreCase(cookieName)) { + String newTestName = cookie.get("value").getAsString(); + if (!newTestName.isEmpty()) { + testName = newTestName; + testInformation.setTestName(testName); + } + } else if(CommonProxyUtilities.metadataCookieName.equalsIgnoreCase(cookieName)) { JsonParser jsonParser = new JsonParser(); JsonObject metadata = jsonParser.parse(cookie.get("value").getAsString()).getAsJsonObject(); @@ -634,6 +656,9 @@ void processContainerAction(final DockerSeleniumContainerAction action, final St if (keepVideoAndLogs()) { if (DockerSeleniumContainerAction.STOP_RECORDING == action) { copyVideos(containerId); + if (stopRecordingByCookie) { + DashboardCollection.updateDashboard(testInformation); + } } if (DockerSeleniumContainerAction.TRANSFER_LOGS == action) { copyLogs(containerId); @@ -660,9 +685,18 @@ void copyVideos(final String containerId) { if (!Files.exists(videoFile.getParent())) { Files.createDirectories(videoFile.getParent()); } + + //For multiple recording in one session, add '_{count}' to the test name + if (Files.exists(videoFile)) { + testInformation.setTestName(testName + "_" + testInformation.getFileCount()); + testInformation.buildVideoFileName(); + videoFile = Paths.get(String.format("%s/%s", testInformation.getVideoFolderPath(), + testInformation.getFileName())); + } Files.copy(entry.get(), videoFile, StandardCopyOption.REPLACE_EXISTING); CommonProxyUtilities.setFilePermissions(videoFile); videoWasCopied = true; + testInformation.setFileCount(testInformation.getFileCount() + 1); LOGGER.debug("Video file copied to: {}/{}", testInformation.getVideoFolderPath(), testInformation.getFileName()); } } catch (IOException e) { @@ -765,22 +799,25 @@ private void unsetCleaningMarker() { private void cleanupNode(boolean willShutdown) { // This basically means that the node is cleaning up and will receive a new request soon // willShutdown == true => there won't be a next session - this.setCleaningMarker(!willShutdown); + if (!isCleaningUp()) { + this.setCleaningMarker(!willShutdown); - try { - if (testInformation != null) { - processContainerAction(DockerSeleniumContainerAction.SEND_NOTIFICATION, + try { + if (testInformation != null) { + processContainerAction(DockerSeleniumContainerAction.SEND_NOTIFICATION, testInformation.getTestStatus().getTestNotificationMessage(), getContainerId()); - } - videoRecording(DockerSeleniumContainerAction.STOP_RECORDING); - processContainerAction(DockerSeleniumContainerAction.TRANSFER_LOGS, getContainerId()); - processContainerAction(DockerSeleniumContainerAction.CLEANUP_CONTAINER, getContainerId()); + } + videoRecording(DockerSeleniumContainerAction.STOP_RECORDING); + processContainerAction(DockerSeleniumContainerAction.TRANSFER_LOGS, getContainerId()); + processContainerAction(DockerSeleniumContainerAction.CLEANUP_CONTAINER, + getContainerId()); - if (testInformation != null && keepVideoAndLogs()) { - DashboardCollection.updateDashboard(testInformation); + if (testInformation != null && keepVideoAndLogs()) { + DashboardCollection.updateDashboard(testInformation); + } + } finally { + this.unsetCleaningMarker(); } - } finally { - this.unsetCleaningMarker(); } } diff --git a/src/test/java/de/zalando/ep/zalenium/it/ParallelIT.java b/src/test/java/de/zalando/ep/zalenium/it/ParallelIT.java index cb7b38b94a..752abcd317 100644 --- a/src/test/java/de/zalando/ep/zalenium/it/ParallelIT.java +++ b/src/test/java/de/zalando/ep/zalenium/it/ParallelIT.java @@ -1,5 +1,7 @@ package de.zalando.ep.zalenium.it; +import org.openqa.selenium.By; +import org.openqa.selenium.Cookie; import org.openqa.selenium.Platform; import org.openqa.selenium.WebDriver; import org.openqa.selenium.net.NetworkUtils; @@ -17,7 +19,9 @@ import java.lang.reflect.Method; import java.net.MalformedURLException; import java.net.URL; +import java.util.concurrent.TimeUnit; +import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.CoreMatchers.containsString; @@ -148,7 +152,8 @@ private WebDriver getWebDriver() { return webDriver.get(); } - @Test(dataProvider = "browsersAndPlatformsForLivePreview") + @SuppressWarnings("groupsTestNG") + @Test(dataProvider = "browsersAndPlatformsForLivePreview", groups = "normal") public void checkIframeLinksForLivePreviewWithMachineIp(DesiredCapabilities desiredCapabilities) { NetworkUtils networkUtils = new NetworkUtils(); @@ -165,8 +170,8 @@ public void checkIframeLinksForLivePreviewWithMachineIp(DesiredCapabilities desi assertThat(pageSource, containsString("view_only=false")); } - - @Test(dataProvider = "browsersAndPlatforms") + @SuppressWarnings("groupsTestNG") + @Test(dataProvider = "browsersAndPlatforms", groups = "normal") public void loadGooglePageAndCheckTitle(DesiredCapabilities desiredCapabilities) { // Go to the homepage @@ -187,4 +192,47 @@ public void loadTheInternetPageAndCheckTitle(DesiredCapabilities desiredCapabili assertThat(getWebDriver().getTitle(), containsString("Internet")); } + @SuppressWarnings("groupsTestNG") + @Test(dataProvider = "browsersAndPlatformsForLivePreview", groups = {"minikube"}) + public void splitVideoRecordingOfOneSessionIntoMultipleFiles(DesiredCapabilities desiredCapabilities) { + + // Go to first page + getWebDriver().manage().timeouts().implicitlyWait(30, TimeUnit.SECONDS); + getWebDriver().get("https://the-internet.herokuapp.com/"); + + // Set test name + String testName = desiredCapabilities.getBrowserName() + "_splitVideoTest"; + Cookie nameCookie = new Cookie("zaleniumTestName", testName); + getWebDriver().manage().addCookie(nameCookie); + + // Stop the video + Cookie stopCookie = new Cookie("zaleniumVideo", "false"); + getWebDriver().manage().addCookie(stopCookie); + + // Start the video + Cookie startCookie = new Cookie("zaleniumVideo", "true"); + getWebDriver().manage().addCookie(startCookie); + + getWebDriver().get("https://www.google.com"); + + // Stop the video + getWebDriver().manage().addCookie(stopCookie); + + // Start the video + getWebDriver().manage().addCookie(startCookie); + + getWebDriver().get("https://www.apple.com"); + + // Stop the video + getWebDriver().manage().addCookie(stopCookie); + + // Go to the dashboard + getWebDriver().get(String.format("http://%s:%s/dashboard", ZALENIUM_HOST, ZALENIUM_PORT)); + + assertThat(getWebDriver().findElements(By.xpath("//small[text()='" + testName + "']")).size(), is(1)); + assertThat(getWebDriver().findElements(By.xpath("//small[text()='" + testName + "_1']")).size(), is(1)); + assertThat(getWebDriver().findElements(By.xpath("//small[text()='" + testName + "_2']")).size(), is(1)); + + } + } diff --git a/src/test/java/de/zalando/ep/zalenium/proxy/DockerSeleniumRemoteProxyTest.java b/src/test/java/de/zalando/ep/zalenium/proxy/DockerSeleniumRemoteProxyTest.java index 80b6cdac8f..a3e10f53f9 100644 --- a/src/test/java/de/zalando/ep/zalenium/proxy/DockerSeleniumRemoteProxyTest.java +++ b/src/test/java/de/zalando/ep/zalenium/proxy/DockerSeleniumRemoteProxyTest.java @@ -557,6 +557,76 @@ public void videoRecordingIsDisabledViaCapability() { Assert.assertFalse(proxy.isVideoRecordingEnabled()); } + @Test + public void videoRecordingStopAndStartViaCookie() { + Map requestedCapability = getCapabilitySupportedByDockerSelenium(); + requestedCapability.put("recordVideo", true); + + DockerSeleniumRemoteProxy spyProxy = spy(proxy); + + // Start pulling thread + spyProxy.startPolling(); + + // Get a test session + TestSession newSession = spyProxy.getNewSession(requestedCapability); + Assert.assertNotNull(newSession); + + // Set cookie. Start with turning video recording off + WebDriverRequest request = mock(WebDriverRequest.class); + HttpServletResponse response = mock(HttpServletResponse.class); + when(request.getMethod()).thenReturn("POST"); + when(request.getRequestType()).thenReturn(RequestType.REGULAR); + when(request.getPathInfo()).thenReturn("/cookie"); + when(request.getBody()).thenReturn("{\"cookie\": {\"name\": \"zaleniumVideo\", \"value\": false}}"); + spyProxy.beforeCommand(newSession, request, response); + + // Assert video recording is stopped + String containerId = spyProxy.getContainerId(); + verify(spyProxy, times(1)) + .videoRecording(DockerSeleniumRemoteProxy.DockerSeleniumContainerAction.STOP_RECORDING); + verify(spyProxy, times(1)) + .processContainerAction(DockerSeleniumRemoteProxy.DockerSeleniumContainerAction.STOP_RECORDING, containerId); + verify(spyProxy, times(1)) + .copyVideos(containerId); + + // Turn the recording back on + when(request.getBody()).thenReturn("{\"cookie\": {\"name\": \"zaleniumVideo\", \"value\": true}}"); + spyProxy.beforeCommand(newSession, request, response); + + // Assert video recording is stopped + verify(spyProxy, times(1)) + .videoRecording(DockerSeleniumRemoteProxy.DockerSeleniumContainerAction.START_RECORDING); + verify(spyProxy, times(1)) + .processContainerAction(DockerSeleniumRemoteProxy.DockerSeleniumContainerAction.START_RECORDING, containerId); + } + + @Test + public void setTestNameViaCookie() { + String testName = "Test"; + + Map requestedCapability = getCapabilitySupportedByDockerSelenium(); + + DockerSeleniumRemoteProxy spyProxy = spy(proxy); + + // Start pulling thread + spyProxy.startPolling(); + + // Get a test session + TestSession newSession = spyProxy.getNewSession(requestedCapability); + Assert.assertNotNull(newSession); + + // Set cookie. Start with turning video recording off + WebDriverRequest request = mock(WebDriverRequest.class); + HttpServletResponse response = mock(HttpServletResponse.class); + when(request.getMethod()).thenReturn("POST"); + when(request.getRequestType()).thenReturn(RequestType.REGULAR); + when(request.getPathInfo()).thenReturn("/cookie"); + when(request.getBody()).thenReturn("{\"cookie\": {\"name\": \"zaleniumTestName\", \"value\": \"" + testName + "\"}}"); + spyProxy.beforeCommand(newSession, request, response); + + Assert.assertEquals(spyProxy.getTestName(), testName); + } + private Map getCapabilitySupportedByDockerSelenium() { Map requestedCapability = new HashMap<>(); requestedCapability.put(CapabilityType.BROWSER_NAME, BrowserType.CHROME);