diff --git a/CHANGELOG.md b/CHANGELOG.md index 23c98bc10..12c5bd505 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Changed + - Hide drawer at the same time as the side panels [#552](https://github.com/zaproxy/zap-hud/issues/552) ## [0.5.0] - 2019-07-24 diff --git a/src/main/java/org/zaproxy/zap/extension/hud/HudParam.java b/src/main/java/org/zaproxy/zap/extension/hud/HudParam.java index fb57afd67..7f220f468 100644 --- a/src/main/java/org/zaproxy/zap/extension/hud/HudParam.java +++ b/src/main/java/org/zaproxy/zap/extension/hud/HudParam.java @@ -31,6 +31,7 @@ import org.zaproxy.zap.eventBus.Event; import org.zaproxy.zap.extension.api.ZapApiIgnore; import org.zaproxy.zap.extension.hud.tutorial.pages.AjaxSpiderPage; +import org.zaproxy.zap.extension.hud.tutorial.pages.HistoryPage; import org.zaproxy.zap.extension.hud.tutorial.pages.HudConfigPage; public class HudParam extends VersionedAbstractParam { @@ -74,7 +75,7 @@ public class HudParam extends VersionedAbstractParam { * However for the HUD we do use it to flag new features, so it will typically be updated for * each new version of the HUD. */ - private static final int PARAM_CURRENT_VERSION = 2; + private static final int PARAM_CURRENT_VERSION = 3; private String baseDirectory; @@ -293,6 +294,9 @@ protected void updateConfigsImpl(int fileVersion) { addTutorialUpdate(AjaxSpiderPage.NAME); addTutorialUpdate(HudConfigPage.NAME); } + if (fileVersion <= 2) { + addTutorialUpdate(HistoryPage.NAME); + } getConfig().setProperty(PARAM_TUTORIAL_UPDATES, tutorialUpdates); saveConfig(); diff --git a/src/main/zapHomeFiles/hud/drawer.html b/src/main/zapHomeFiles/hud/drawer.html index aca990b05..cbf0464d8 100644 --- a/src/main/zapHomeFiles/hud/drawer.html +++ b/src/main/zapHomeFiles/hud/drawer.html @@ -14,10 +14,10 @@
- + - + @@ -127,19 +127,19 @@
    -
  • - {{ tab.name }} +
  • + {{ tab.name }}
  • -
  • +
  • -
  • +
  • -
  • +
  • diff --git a/src/main/zapHomeFiles/hud/drawer.js b/src/main/zapHomeFiles/hud/drawer.js index 44c5341e8..4f5210826 100644 --- a/src/main/zapHomeFiles/hud/drawer.js +++ b/src/main/zapHomeFiles/hud/drawer.js @@ -230,7 +230,8 @@ Vue.component('tabs', { return { tabs: [], isOpen: false, - isArrowUp: true + isArrowUp: true, + tabsVisible: true }; }, methods: { @@ -299,12 +300,27 @@ Vue.component('tabs', { } }) .catch(utils.errorHandler); + + eventBus.$on('showTabs', data => { + this.tabsVisible = true; + }) + eventBus.$on('hideTabs', data => { + this.tabsVisible = false; + if (this.isOpen) { + this.closeDrawer(); + } + }) + }, + beforeDestroy () { + eventBus.$off('hideTabs') + eventBus.$off('showTabs') } }); Vue.component('tab', { template: '#tab-template', props: { + id: { required: true }, name: { required: true }, selected: { default: false } }, @@ -400,17 +416,24 @@ Vue.component('drawer-button-showhide', { methods: { showHud() { this.isHudVisible = true; - this.icon = utils.getZapImagePath('radar.png'); localforage.setItem('settings.isHudVisible', true) + .then(function(value){ + this.icon = utils.getZapImagePath('radar.png'); + parent.postMessage({tabId: tabId, frameId: frameId, action:'showHudPanels'}, document.referrer); + eventBus.$emit('showTabs', {}); + }) .catch(utils.errorHandler); - parent.postMessage({tabId: tabId, frameId: frameId, action:'showSidePanels'}, document.referrer); + }, hideHud() { this.isHudVisible = false; - this.icon = utils.getZapImagePath('radar-grey.png'); localforage.setItem('settings.isHudVisible', false) + .then(function(value){ + this.icon = utils.getZapImagePath('radar-grey.png'); + parent.postMessage({tabId: tabId, frameId: frameId, action:'hideHudPanels'}, document.referrer); + eventBus.$emit('hideTabs', {}); + }) .catch(utils.errorHandler); - parent.postMessage({tabId: tabId, frameId: frameId, action:'hideSidePanels'}, document.referrer); }, toggleIsVisible() { this.isHudVisible ? this.hideHud() : this.showHud(); @@ -423,6 +446,7 @@ Vue.component('drawer-button-showhide', { this.isHudVisible = isHudVisible; if (!this.isHudVisible) { this.icon = utils.getZapImagePath('radar-grey.png'); + eventBus.$emit('hideTabs', {}); } }) .catch(utils.errorHandler); diff --git a/src/main/zapHomeFiles/hud/panel.js b/src/main/zapHomeFiles/hud/panel.js index 4ed0963b9..a76a12948 100644 --- a/src/main/zapHomeFiles/hud/panel.js +++ b/src/main/zapHomeFiles/hud/panel.js @@ -143,7 +143,7 @@ Vue.component('hud-buttons', { localforage.getItem('settings.isHudVisible') .then(isHudVisible => { if (isHudVisible !== null && !isHudVisible) { - return parent.postMessage({action:'hideSidePanels'}, document.referrer); + return parent.postMessage({action:'hideHudPanels'}, document.referrer); } }) .then( () => { diff --git a/src/main/zapHomeFiles/hud/target/inject.js b/src/main/zapHomeFiles/hud/target/inject.js index 91b1f474e..fad489107 100644 --- a/src/main/zapHomeFiles/hud/target/inject.js +++ b/src/main/zapHomeFiles/hud/target/inject.js @@ -85,14 +85,22 @@ panel.style.height = (panel.offsetHeight - 33) + "px"; } - function showSidePanels() { + function showHudPanels() { document.getElementById(LEFT_PANEL).style.display = ""; document.getElementById(RIGHT_PANEL).style.display = ""; + var panel = document.getElementById(BOTTOM_DRAWER); + panel.style.width = "100%"; + panel.style.left = "0px"; + panel.style.right = ""; } - function hideSidePanels() { + function hideHudPanels() { document.getElementById(LEFT_PANEL).style.display = "none"; document.getElementById(RIGHT_PANEL).style.display = "none"; + var panel = document.getElementById(BOTTOM_DRAWER); + panel.style.width = "90px"; + panel.style.left = ""; + panel.style.right = "0px"; } function hideAllDisplayFrames() { @@ -319,12 +327,12 @@ showPanel(message.orientation); break; - case "showSidePanels": - showSidePanels(); + case "showHudPanels": + showHudPanels(); break; - case "hideSidePanels": - hideSidePanels(); + case "hideHudPanels": + hideHudPanels(); break; case "showMainDisplay": diff --git a/src/main/zapHomeFiles/hudtutorial/en_GB/History.html b/src/main/zapHomeFiles/hudtutorial/en_GB/History.html index b547195ce..1de472c32 100644 --- a/src/main/zapHomeFiles/hudtutorial/en_GB/History.html +++ b/src/main/zapHomeFiles/hudtutorial/en_GB/History.html @@ -18,6 +18,9 @@

    History

    You can click on the tab or on the arrow button on the right hand side to show and hide the list of the requests, and you can click on any of the requests to see full details of the requests and responses. +

    + The green HUD icon will now also hide all of the tabs in this frame in case they are obscuring + content as well. diff --git a/src/test/java/org/zaproxy/zap/extension/hud/ui/firefox/badsite/BadSiteUnitTest.java b/src/test/java/org/zaproxy/zap/extension/hud/ui/firefox/badsite/BadSiteUnitTest.java index 1729c2279..d984cf847 100644 --- a/src/test/java/org/zaproxy/zap/extension/hud/ui/firefox/badsite/BadSiteUnitTest.java +++ b/src/test/java/org/zaproxy/zap/extension/hud/ui/firefox/badsite/BadSiteUnitTest.java @@ -25,7 +25,6 @@ import static org.junit.jupiter.api.Assertions.fail; import java.io.IOException; -import java.io.InputStream; import java.net.MalformedURLException; import java.net.URL; import javax.net.ssl.HttpsURLConnection; @@ -33,7 +32,6 @@ import javax.net.ssl.TrustManager; import javax.net.ssl.X509TrustManager; import net.sf.json.JSONObject; -import org.apache.commons.io.IOUtils; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; @@ -43,10 +41,8 @@ import org.openqa.selenium.WebElement; import org.openqa.selenium.firefox.FirefoxDriver; import org.openqa.selenium.support.ui.WebDriverWait; -import org.zaproxy.zap.extension.api.API; import org.zaproxy.zap.extension.hud.HudAPI; import org.zaproxy.zap.extension.hud.tutorial.pages.IntroPage; -import org.zaproxy.zap.extension.hud.ui.Constants; import org.zaproxy.zap.extension.hud.ui.firefox.FirefoxUnitTest; import org.zaproxy.zap.extension.hud.ui.firefox.tutorial.TutorialStatics; import org.zaproxy.zap.extension.hud.ui.uimap.HUD; @@ -126,7 +122,8 @@ public void cannotUseZapApiFromTarget(FirefoxDriver driver) hud.openUrlWaitForHud(TutorialStatics.getTutorialUrl(IntroPage.NAME)); // Test the value is empty to start - JSONObject ret = callApi("/JSON/hud/view/getUiOption/?key=" + BAD_SITE_TEST_KEY + "&"); + JSONObject ret = + HUD.callZapApi("/JSON/hud/view/getUiOption/?key=" + BAD_SITE_TEST_KEY + "&"); assertEquals("", ret.get(BAD_SITE_TEST_KEY)); String script = @@ -146,25 +143,10 @@ public void cannotUseZapApiFromTarget(FirefoxDriver driver) Thread.sleep(5000); // Just to make sure // And check its still empty - ret = callApi("/JSON/hud/view/getUiOption/?key=" + BAD_SITE_TEST_KEY + "&"); + ret = HUD.callZapApi("/JSON/hud/view/getUiOption/?key=" + BAD_SITE_TEST_KEY + "&"); assertEquals("", ret.get(BAD_SITE_TEST_KEY)); } - private static JSONObject callApi(String apiCall) throws MalformedURLException, IOException { - String apiUrl = - "http://" - + Constants.ZAP_HOST_PORT - + apiCall - + API.API_KEY_PARAM - + "=" - + Constants.ZAP_TEST_API_KEY; - - try (InputStream in = new URL(apiUrl).openStream()) { - String str = IOUtils.toString(in, "UTF-8"); - return JSONObject.fromObject(str); - } - } - private static Object executeScriptWithRetry(WebDriver driver, String script) { return new WebDriverWait(driver, 10L) .ignoring(JavascriptException.class) diff --git a/src/test/java/org/zaproxy/zap/extension/hud/ui/firefox/tutorial/FramesPageUnitTest.java b/src/test/java/org/zaproxy/zap/extension/hud/ui/firefox/tutorial/FramesPageUnitTest.java index 4cc3bd9bf..233698acd 100644 --- a/src/test/java/org/zaproxy/zap/extension/hud/ui/firefox/tutorial/FramesPageUnitTest.java +++ b/src/test/java/org/zaproxy/zap/extension/hud/ui/firefox/tutorial/FramesPageUnitTest.java @@ -64,7 +64,8 @@ public void testPreviousButtonWorks(FirefoxDriver driver) { } @Test - public void testTaskAndNextButton(FirefoxDriver driver) { + public void testTaskAndNextButton(FirefoxDriver driver) throws Exception { + HUD.callZapApiResetTasks(); HUD hud = new HUD(driver); hud.openUrlWaitForHud(TutorialStatics.getTutorialUrl(FramesPage.NAME)); @@ -98,7 +99,8 @@ public void testTaskAndNextButton(FirefoxDriver driver) { } @Test - public void testSidePanelsHiddenAndRevealed(FirefoxDriver driver) throws URISyntaxException { + public void testSidePanelsHiddenAndRevealed(FirefoxDriver driver) throws Exception { + HUD.callZapApiResetTasks(); HUD hud = new HUD(driver); hud.openUrlWaitForHud(TutorialStatics.getTutorialUrl(FramesPage.NAME)); @@ -106,7 +108,7 @@ public void testSidePanelsHiddenAndRevealed(FirefoxDriver driver) throws URISynt testSidePanesVisible(hud); // Check they are hidden after button clicked - testClickHudButton(driver); + testClickHudButton(driver, false); testSidePanesHidden(hud); // Check they stay hidden when the page is refreshed @@ -115,10 +117,33 @@ public void testSidePanelsHiddenAndRevealed(FirefoxDriver driver) throws URISynt testSidePanesHidden(hud); // Check they are revealed when the button is clicked again - testClickHudButton(driver); + testClickHudButton(driver, true); testSidePanesVisible(hud); } + @Test + public void testBottonDrawerTabsHiddenAndRevealed(FirefoxDriver driver) + throws URISyntaxException { + HUD hud = new HUD(driver); + hud.openUrlWaitForHud(TutorialStatics.getTutorialUrl(FramesPage.NAME)); + + // check they visible to start with + testDrawerTabsVisible(hud); + + // Check they are hidden after button clicked + testClickHudButton(driver, false); + testDrawerTabsHidden(hud); + + // Check they stay hidden when the page is refreshed + hud.openRelativePage(FramesPage.NAME); + + testDrawerTabsHidden(hud); + + // Check they are revealed when the button is clicked again + testClickHudButton(driver, true); + testDrawerTabsVisible(hud); + } + private static void checkPanelVisible(WebDriver wd, WebElement panel) { checkWithRetry(wd, driver -> panel.isDisplayed()); Assertions.assertEquals("block", panel.getCssValue("display")); @@ -136,10 +161,11 @@ private static void testSidePanesVisible(HUD hud) { checkPanelVisible(wd, hud.waitForBottomPanel()); } - private static void testClickHudButton(WebDriver wd) { + private static void testClickHudButton(WebDriver wd, boolean hidden) { HUD hud = new HUD(wd); - List buttons = hud.waitForHudButtons(HUD.BOTTOM_PANEL_BY_ID, 2); - Assertions.assertEquals(2, buttons.size()); + int expectedButtons = hidden ? 1 : 2; + List buttons = hud.waitForHudButtons(HUD.BOTTOM_PANEL_BY_ID, expectedButtons); + Assertions.assertEquals(expectedButtons, buttons.size()); buttons.get(0).click(); wd.switchTo().defaultContent(); @@ -156,4 +182,30 @@ private static void testSidePanesHidden(HUD hud) { checkPanelHidden(wd, hud.waitForRightPanel()); checkPanelVisible(wd, hud.waitForBottomPanel()); } + + private void testDrawerTabsVisible(HUD hud) { + WebDriver wd = hud.getWebDriver(); + hud.waitForHudButtons(HUD.BOTTOM_PANEL_BY_ID, 2); + Assertions.assertNotNull(wd.findElement(By.id(HUD.BOTTOM_TAB_HISTORY_ID))); + Assertions.assertNotNull(wd.findElement(By.id(HUD.BOTTOM_TAB_WEBSOCKETS_ID))); + wd.switchTo().parentFrame(); + } + + private void testDrawerTabsHidden(HUD hud) { + WebDriver wd = hud.getWebDriver(); + hud.waitForHudButtons(HUD.BOTTOM_PANEL_BY_ID, 1); + try { + wd.findElement(By.id(HUD.BOTTOM_TAB_HISTORY_ID)); + fail("History tab should have been hidden"); + } catch (NoSuchElementException e) { + // Expected + } + try { + wd.findElement(By.id(HUD.BOTTOM_TAB_WEBSOCKETS_ID)); + fail("Websockets tab should have been hidden"); + } catch (NoSuchElementException e) { + // Expected + } + wd.switchTo().parentFrame(); + } } diff --git a/src/test/java/org/zaproxy/zap/extension/hud/ui/uimap/HUD.java b/src/test/java/org/zaproxy/zap/extension/hud/ui/uimap/HUD.java index 77b16064c..58e01e033 100644 --- a/src/test/java/org/zaproxy/zap/extension/hud/ui/uimap/HUD.java +++ b/src/test/java/org/zaproxy/zap/extension/hud/ui/uimap/HUD.java @@ -19,11 +19,17 @@ */ package org.zaproxy.zap.extension.hud.ui.uimap; +import java.io.IOException; +import java.io.InputStream; +import java.net.MalformedURLException; import java.net.URI; import java.net.URISyntaxException; +import java.net.URL; import java.util.Date; import java.util.List; import java.util.function.Function; +import net.sf.json.JSONObject; +import org.apache.commons.io.IOUtils; import org.openqa.selenium.By; import org.openqa.selenium.JavascriptExecutor; import org.openqa.selenium.WebDriver; @@ -32,6 +38,7 @@ import org.openqa.selenium.support.ui.ExpectedConditions; import org.openqa.selenium.support.ui.Wait; import org.openqa.selenium.support.ui.WebDriverWait; +import org.zaproxy.zap.extension.api.API; import org.zaproxy.zap.extension.hud.ui.Constants; /** @@ -52,6 +59,8 @@ public HUD(WebDriver webdriver) { public static String DISPLAY_PANEL_ID = "zap-hud-main-display"; public static String MANAGEMENT_PANEL_ID = "zap-hud-management"; public static String HISTORY_TABLE_ID = "history-messages"; + public static String BOTTOM_TAB_HISTORY_ID = "tab.history"; + public static String BOTTOM_TAB_WEBSOCKETS_ID = "tab.websockets"; public static By LEFT_PANEL_BY_ID = By.id(LEFT_PANEL_ID); public static By RIGHT_PANEL_BY_ID = By.id(RIGHT_PANEL_ID); @@ -220,6 +229,31 @@ public Boolean apply(WebDriver driver) { }); } + public static JSONObject callZapApi(String apiCall) throws MalformedURLException, IOException { + String apiUrl = + "http://" + + Constants.ZAP_HOST_PORT + + apiCall + + API.API_KEY_PARAM + + "=" + + Constants.ZAP_TEST_API_KEY; + + try (InputStream in = new URL(apiUrl).openStream()) { + String str = IOUtils.toString(in, "UTF-8"); + return JSONObject.fromObject(str); + } + } + + /** + * Resets all of the tutorial tasks - can be used where multiple tests can clear the same task. + * + * @throws MalformedURLException + * @throws IOException + */ + public static void callZapApiResetTasks() throws MalformedURLException, IOException { + HUD.callZapApi("/JSON/hud/action/resetTutorialTasks?"); + } + public WebDriver getWebDriver() { return webdriver; }