diff --git a/src/main/resources/templates/charts/charts.ftl b/src/main/resources/templates/charts/charts.ftl index a31298d04a382b163528ca3ec806f72c8d803138..93465fdccd2bdc514334fbb3e6869c42afd6f190 100644 --- a/src/main/resources/templates/charts/charts.ftl +++ b/src/main/resources/templates/charts/charts.ftl @@ -228,7 +228,7 @@ </#macro> <#macro filterOptions> - <div class="container"> + <div class="container" id="chart-filter-container"> <div class="row"> <div class="col s12 no-margin-top center-align"> <ul class="collapsible"> diff --git a/src/test/java/de/deadlocker8/budgetmaster/integration/selenium/ChartTest.java b/src/test/java/de/deadlocker8/budgetmaster/integration/selenium/ChartTest.java new file mode 100644 index 0000000000000000000000000000000000000000..cecbe0605d8df574a033b125eed3d9848de61ec3 --- /dev/null +++ b/src/test/java/de/deadlocker8/budgetmaster/integration/selenium/ChartTest.java @@ -0,0 +1,361 @@ +package de.deadlocker8.budgetmaster.integration.selenium; + +import de.deadlocker8.budgetmaster.Main; +import de.deadlocker8.budgetmaster.accounts.Account; +import de.deadlocker8.budgetmaster.accounts.AccountType; +import de.deadlocker8.budgetmaster.authentication.UserService; +import de.deadlocker8.budgetmaster.charts.ChartDisplayType; +import de.deadlocker8.budgetmaster.charts.ChartGroupType; +import de.deadlocker8.budgetmaster.integration.helpers.IntegrationTestHelper; +import de.deadlocker8.budgetmaster.integration.helpers.SeleniumTest; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TestName; +import org.junit.rules.TestWatcher; +import org.junit.runner.Description; +import org.junit.runner.RunWith; +import org.openqa.selenium.By; +import org.openqa.selenium.JavascriptExecutor; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.firefox.FirefoxDriver; +import org.openqa.selenium.firefox.FirefoxOptions; +import org.openqa.selenium.support.ui.ExpectedConditions; +import org.openqa.selenium.support.ui.WebDriverWait; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.web.server.LocalServerPort; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.junit4.SpringRunner; + +import java.io.File; +import java.util.Arrays; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +@RunWith(SpringRunner.class) +@SpringBootTest(classes = Main.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD) +@SeleniumTest +public class ChartTest +{ + private final String SELECTOR_ACTIVE_DISPLAY_TYPE = ".button-display-type.active"; + private final String SELECTOR_ACTIVE_GROUP_TYPE = ".button-group-type.active"; + private final String SELECTOR_VISIBLE_CHART_PREVIEWS = ".chart-preview-column:not(.hidden)"; + private final String SELECTOR_ACTIVE_CHART_PREVIEWS = ".chart-preview.active"; + + + private IntegrationTestHelper helper; + private WebDriver driver; + + @LocalServerPort + int port; + + @Rule + public TestName name = new TestName(); + + @Rule + public TestWatcher testWatcher = new TestWatcher() + { + @Override + protected void finished(Description description) + { + driver.quit(); + } + + @Override + protected void failed(Throwable e, Description description) + { + IntegrationTestHelper.saveScreenshots(driver, name, ChartTest.class); + } + }; + + @Before + public void prepare() + { + FirefoxOptions options = new FirefoxOptions(); + options.setHeadless(false); + options.addPreference("devtools.console.stdout.content", true); + driver = new FirefoxDriver(options); + + // prepare + helper = new IntegrationTestHelper(driver, port); + helper.start(); + helper.login(UserService.DEFAULT_PASSWORD); + helper.hideBackupReminder(); + helper.hideWhatsNewDialog(); + + String path = getClass().getClassLoader().getResource("SearchDatabase.json").getFile().replace("/", File.separator); + final Account account1 = new Account("DefaultAccount0815", AccountType.CUSTOM); + final Account account2 = new Account("Account2", AccountType.CUSTOM); + + helper.uploadDatabase(path, Arrays.asList("DefaultAccount0815", "sfsdf"), List.of(account1, account2)); + } + + @Test + public void test_defaultSelection() + { + driver.get(helper.getUrl() + "/charts"); + + // check display type + assertThat(getSelectedType(SELECTOR_ACTIVE_DISPLAY_TYPE)).isEqualTo(ChartDisplayType.BAR.name()); + assertThat(driver.findElement(By.id("buttonCustomCharts")).isDisplayed()).isFalse(); + + // check group type + assertThat(driver.findElement(By.id("chart-group-type-buttons")).isDisplayed()).isTrue(); + assertThat(getSelectedType(SELECTOR_ACTIVE_GROUP_TYPE)).isEqualTo(ChartGroupType.MONTH.name()); + + // check displayed chart previews + final List<WebElement> displayedChartPreviews = driver.findElements(By.cssSelector(SELECTOR_VISIBLE_CHART_PREVIEWS)); + assertThat(displayedChartPreviews) + .hasSize(3); + assertThat(displayedChartPreviews.get(0).findElement(By.cssSelector(".card-action span")).getText()) + .isEqualTo("Incomes/Expenditures per month"); + + // filter + assertThat(driver.findElement(By.id("filterActiveBadge")).isDisplayed()).isFalse(); + + // button + assertThat(driver.findElement(By.name("buttonSave")).isEnabled()).isFalse(); + } + + @Test + public void test_selectDisplayType() + { + driver.get(helper.getUrl() + "/charts"); + + final String buttonSelector = ".button-display-type[data-value='" + ChartDisplayType.LINE.name() + "']"; + driver.findElement(By.cssSelector(buttonSelector)).click(); + + WebDriverWait wait = new WebDriverWait(driver, 5); + wait.until(ExpectedConditions.attributeContains(By.cssSelector(buttonSelector), "class", "active")); + + // check display type + assertThat(getSelectedType(SELECTOR_ACTIVE_DISPLAY_TYPE)).isEqualTo(ChartDisplayType.LINE.name()); + assertThat(driver.findElement(By.id("buttonCustomCharts")).isDisplayed()).isFalse(); + + // check group type + assertThat(driver.findElement(By.id("chart-group-type-buttons")).isDisplayed()).isTrue(); + assertThat(getSelectedType(SELECTOR_ACTIVE_GROUP_TYPE)).isEqualTo(ChartGroupType.MONTH.name()); + + // check displayed chart previews + final List<WebElement> displayedChartPreviews = driver.findElements(By.cssSelector(SELECTOR_VISIBLE_CHART_PREVIEWS)); + assertThat(displayedChartPreviews) + .hasSize(1); + assertThat(displayedChartPreviews.get(0).findElement(By.cssSelector(".card-action span")).getText()) + .isEqualTo("Incomes/Expenditures per month"); + + // filter + assertThat(driver.findElement(By.id("filterActiveBadge")).isDisplayed()).isFalse(); + + // button + assertThat(driver.findElement(By.name("buttonSave")).isEnabled()).isFalse(); + } + + @Test + public void test_hideGroupTypeIfOnlyOneDistinct() + { + driver.get(helper.getUrl() + "/charts"); + + final String buttonSelector = ".button-display-type[data-value='" + ChartDisplayType.PIE.name() + "']"; + driver.findElement(By.cssSelector(buttonSelector)).click(); + + WebDriverWait wait = new WebDriverWait(driver, 5); + wait.until(ExpectedConditions.attributeContains(By.cssSelector(buttonSelector), "class", "active")); + + // check display type + assertThat(getSelectedType(SELECTOR_ACTIVE_DISPLAY_TYPE)).isEqualTo(ChartDisplayType.PIE.name()); + + // check group type + assertThat(driver.findElement(By.id("chart-group-type-buttons")).isDisplayed()).isFalse(); + } + + @Test + public void test_displayGroupTypeAfterHiding() + { + driver.get(helper.getUrl() + "/charts"); + + String buttonSelector = ".button-display-type[data-value='" + ChartDisplayType.PIE.name() + "']"; + driver.findElement(By.cssSelector(buttonSelector)).click(); + + WebDriverWait wait = new WebDriverWait(driver, 5); + wait.until(ExpectedConditions.attributeContains(By.cssSelector(buttonSelector), "class", "active")); + + // check display type + assertThat(getSelectedType(SELECTOR_ACTIVE_DISPLAY_TYPE)).isEqualTo(ChartDisplayType.PIE.name()); + + // check group type + assertThat(driver.findElement(By.id("chart-group-type-buttons")).isDisplayed()).isFalse(); + + buttonSelector = ".button-display-type[data-value='" + ChartDisplayType.BAR.name() + "']"; + driver.findElement(By.cssSelector(buttonSelector)).click(); + + wait = new WebDriverWait(driver, 5); + wait.until(ExpectedConditions.attributeContains(By.cssSelector(buttonSelector), "class", "active")); + + // check display type + assertThat(getSelectedType(SELECTOR_ACTIVE_DISPLAY_TYPE)).isEqualTo(ChartDisplayType.BAR.name()); + + // check group type + assertThat(driver.findElement(By.id("chart-group-type-buttons")).isDisplayed()).isTrue(); + } + + @Test + public void test_selectGroupType() + { + driver.get(helper.getUrl() + "/charts"); + + final String buttonSelector = ".button-group-type[data-value='" + ChartGroupType.YEAR.name() + "']"; + driver.findElement(By.cssSelector(buttonSelector)).click(); + + WebDriverWait wait = new WebDriverWait(driver, 5); + wait.until(ExpectedConditions.attributeContains(By.cssSelector(buttonSelector), "class", "active")); + + // check group type + assertThat(driver.findElement(By.id("chart-group-type-buttons")).isDisplayed()).isTrue(); + assertThat(getSelectedType(SELECTOR_ACTIVE_GROUP_TYPE)).isEqualTo(ChartGroupType.YEAR.name()); + + // check displayed chart previews + final List<WebElement> displayedChartPreviews = driver.findElements(By.cssSelector(SELECTOR_VISIBLE_CHART_PREVIEWS)); + assertThat(displayedChartPreviews) + .hasSize(1); + assertThat(displayedChartPreviews.get(0).findElement(By.cssSelector(".card-action span")).getText()) + .isEqualTo("Incomes/Expenditures per year"); + + // filter + assertThat(driver.findElement(By.id("filterActiveBadge")).isDisplayed()).isFalse(); + + // button + assertThat(driver.findElement(By.name("buttonSave")).isEnabled()).isFalse(); + } + + @Test + public void test_selectChartEnabledButton() + { + driver.get(helper.getUrl() + "/charts"); + + final String chartPreviewSelector = ".chart-preview-column[data-id='6']"; + driver.findElement(By.cssSelector(chartPreviewSelector)).click(); + + final WebDriverWait wait = new WebDriverWait(driver, 5); + wait.until(ExpectedConditions.attributeContains(By.cssSelector(chartPreviewSelector + " .chart-preview"), "class", "active")); + + // check displayed chart previews + final List<WebElement> activeChartPreviews = driver.findElements(By.cssSelector(SELECTOR_ACTIVE_CHART_PREVIEWS)); + assertThat(activeChartPreviews) + .hasSize(1); + assertThat(activeChartPreviews.get(0).findElement(By.cssSelector(".card-action span")).getText()) + .isEqualTo("Incomes/Expenditures per month by categories"); + + // button + assertThat(driver.findElement(By.name("buttonSave")).isEnabled()).isTrue(); + } + + @Test + public void test_selectDisplayTypeAfterSelectingChartDisablesButton() + { + driver.get(helper.getUrl() + "/charts"); + + final String chartPreviewSelector = ".chart-preview-column[data-id='6']"; + driver.findElement(By.cssSelector(chartPreviewSelector)).click(); + + WebDriverWait wait = new WebDriverWait(driver, 5); + wait.until(ExpectedConditions.attributeContains(By.cssSelector(chartPreviewSelector + " .chart-preview"), "class", "active")); + + final String buttonSelector = ".button-display-type[data-value='" + ChartDisplayType.LINE.name() + "']"; + driver.findElement(By.cssSelector(buttonSelector)).click(); + + wait = new WebDriverWait(driver, 5); + wait.until(ExpectedConditions.attributeContains(By.cssSelector(buttonSelector), "class", "active")); + + // check displayed chart previews + final List<WebElement> activeChartPreviews = driver.findElements(By.cssSelector(SELECTOR_ACTIVE_CHART_PREVIEWS)); + assertThat(activeChartPreviews).isEmpty(); + + // button + assertThat(driver.findElement(By.name("buttonSave")).isEnabled()).isFalse(); + } + + @Test + public void test_showFilterBadge() + { + driver.get(helper.getUrl() + "/charts"); + + assertThat(driver.findElement(By.id("filterActiveBadge")).isDisplayed()).isFalse(); + + driver.findElement(By.id("chart-filter-container")).click(); + driver.findElement(By.id("section-type")).click(); + final WebElement checkBox = driver.findElement(By.cssSelector("#section-type .text-default")); + ((JavascriptExecutor) driver).executeScript("arguments[0].scrollIntoView(true);", checkBox); + checkBox.click(); + + final WebDriverWait wait = new WebDriverWait(driver, 5); + wait.until(ExpectedConditions.visibilityOfElementLocated(By.className("filter-button-reset"))); + + assertThat(driver.findElement(By.id("filterActiveBadge")).isDisplayed()).isTrue(); + } + + @Test + public void test_hideFilterBadgeOnReset() + { + driver.get(helper.getUrl() + "/charts"); + + driver.findElement(By.id("chart-filter-container")).click(); + driver.findElement(By.id("section-type")).click(); + final WebElement checkBox = driver.findElement(By.cssSelector("#section-type .text-default")); + ((JavascriptExecutor) driver).executeScript("arguments[0].scrollIntoView(true);", checkBox); + checkBox.click(); + + final WebDriverWait wait = new WebDriverWait(driver, 5); + wait.until(ExpectedConditions.visibilityOfElementLocated(By.className("filter-button-reset"))); + + driver.findElement(By.className("filter-button-reset")).click(); + assertThat(driver.findElement(By.id("filterActiveBadge")).isDisplayed()).isFalse(); + } + + @Test + public void test_showManageButtonForCustomCharts() + { + driver.get(helper.getUrl() + "/charts"); + + final String buttonSelector = ".button-display-type[data-value='" + ChartDisplayType.CUSTOM.name() + "']"; + driver.findElement(By.cssSelector(buttonSelector)).click(); + + WebDriverWait wait = new WebDriverWait(driver, 5); + wait.until(ExpectedConditions.attributeContains(By.cssSelector(buttonSelector), "class", "active")); + + // check display type + assertThat(getSelectedType(SELECTOR_ACTIVE_DISPLAY_TYPE)).isEqualTo(ChartDisplayType.CUSTOM.name()); + + assertThat(driver.findElement(By.id("buttonCustomCharts")).isDisplayed()).isTrue(); + } + + @Test + public void test_showChart() + { + driver.get(helper.getUrl() + "/charts"); + + final String chartPreviewSelector = ".chart-preview-column[data-id='6']"; + driver.findElement(By.cssSelector(chartPreviewSelector)).click(); + + WebDriverWait wait = new WebDriverWait(driver, 5); + wait.until(ExpectedConditions.attributeContains(By.cssSelector(chartPreviewSelector + " .chart-preview"), "class", "active")); + + driver.findElement(By.name("buttonSave")).click(); + + wait = new WebDriverWait(driver, 5); + wait.until(ExpectedConditions.visibilityOfElementLocated(By.className("chart-canvas"))); + + assertThat(driver.findElements(By.cssSelector(".chart-canvas .plot-container"))).hasSize(1); + } + + private String getSelectedType(String selector) + { + final List<WebElement> activeTypeButtons = driver.findElements(By.cssSelector(selector)); + assertThat(activeTypeButtons) + .hasSize(1); + + return activeTypeButtons.get(0).getAttribute("data-value"); + } +} \ No newline at end of file