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