diff --git a/pom.xml b/pom.xml index bdfab318493ab9723c1507a2f4a8c7f06704fc95..028d4e0b3490679de4956e50e419a4e14707ddeb 100644 --- a/pom.xml +++ b/pom.xml @@ -195,6 +195,12 @@ <version>${selenium.version}</version> <scope>test</scope> </dependency> + <dependency> + <groupId>org.seleniumhq.selenium</groupId> + <artifactId>selenium-support</artifactId> + <version>${selenium.version}</version> + <scope>test</scope> + </dependency> </dependencies> <build> @@ -272,6 +278,10 @@ <configuration> <junitArtifactName>junit:junit</junitArtifactName> <argLine>-Dfile.encoding=UTF-8</argLine> + + <systemPropertyVariables> + <testProfile>true</testProfile> + </systemPropertyVariables> </configuration> </plugin> diff --git a/src/main/java/de/deadlocker8/budgetmaster/Main.java b/src/main/java/de/deadlocker8/budgetmaster/Main.java index 4a059ba5a29c430935086711ec34e511abd1e008..da74dae2b34fc4e81cbe27e894de612b5cc90256 100644 --- a/src/main/java/de/deadlocker8/budgetmaster/Main.java +++ b/src/main/java/de/deadlocker8/budgetmaster/Main.java @@ -83,13 +83,29 @@ public class Main extends SpringBootServletInitializer implements ApplicationRun public static Path getApplicationSupportFolder() { - if(ProgramArgs.isDebug()) + if(System.getProperties().containsKey("testProfile")) { - LOGGER.info("Starting in DEBUG Mode"); - return SystemUtils.getApplicationSupportDirectoryPath(Localization.getString("folder"), "debug"); + RunMode.currentRunMode = RunMode.TEST; + } + else if(ProgramArgs.isDebug()) + { + RunMode.currentRunMode = RunMode.DEBUG; } - return SystemUtils.getApplicationSupportDirectoryPath(Localization.getString("folder")); + switch(RunMode.currentRunMode) + { + case NORMAL: + LOGGER.info("Starting in NORMAL Mode"); + return SystemUtils.getApplicationSupportDirectoryPath(Localization.getString("folder")); + case DEBUG: + LOGGER.info("Starting in DEBUG Mode"); + return SystemUtils.getApplicationSupportDirectoryPath(Localization.getString("folder"), "debug"); + case TEST: + LOGGER.info("Starting in TEST Mode"); + return SystemUtils.getApplicationSupportDirectoryPath(Localization.getString("folder"), "test"); + default: + return null; + } } public static void main(String[] args) @@ -115,17 +131,24 @@ public class Main extends SpringBootServletInitializer implements ApplicationRun { Build build = Build.getInstance(); logAppInfo(build.getAppName(), build.getVersionName(), build.getVersionCode(), build.getVersionDate()); - if(ProgramArgs.isDebug()) - { - LOGGER.info("=================================="); - LOGGER.info("+++ BUDGETMASTER DEBUG STARTED +++"); - LOGGER.info("=================================="); - } - else + + switch(RunMode.currentRunMode) { - LOGGER.info("============================="); - LOGGER.info("+++ BUDGETMASTER STARTED +++"); - LOGGER.info("============================="); + case NORMAL: + LOGGER.info("============================="); + LOGGER.info("+++ BUDGETMASTER STARTED +++"); + LOGGER.info("============================="); + break; + case DEBUG: + LOGGER.info("=================================="); + LOGGER.info("+++ BUDGETMASTER DEBUG STARTED +++"); + LOGGER.info("=================================="); + break; + case TEST: + LOGGER.info("================================="); + LOGGER.info("+++ BUDGETMASTER TEST STARTED +++"); + LOGGER.info("================================="); + break; } } diff --git a/src/main/java/de/deadlocker8/budgetmaster/ProgramArgs.java b/src/main/java/de/deadlocker8/budgetmaster/ProgramArgs.java index 6e764de754aadd3c1ec86f2728ec11ca69b3900f..972706c9842879585190307f8299dd6865717a01 100644 --- a/src/main/java/de/deadlocker8/budgetmaster/ProgramArgs.java +++ b/src/main/java/de/deadlocker8/budgetmaster/ProgramArgs.java @@ -26,4 +26,9 @@ public class ProgramArgs { return ProgramArgs.getArgs().contains("--debugFolder"); } + + public static boolean isTest() + { + return RunMode.currentRunMode.equals(RunMode.TEST); + } } \ No newline at end of file diff --git a/src/main/java/de/deadlocker8/budgetmaster/RunMode.java b/src/main/java/de/deadlocker8/budgetmaster/RunMode.java new file mode 100644 index 0000000000000000000000000000000000000000..94defc81da4248f3ea1969b706f75cf813aa2690 --- /dev/null +++ b/src/main/java/de/deadlocker8/budgetmaster/RunMode.java @@ -0,0 +1,10 @@ +package de.deadlocker8.budgetmaster; + +public enum RunMode +{ + NORMAL, + DEBUG, + TEST; + + public static RunMode currentRunMode = NORMAL; +} diff --git a/src/main/java/de/deadlocker8/budgetmaster/authentication/UserService.java b/src/main/java/de/deadlocker8/budgetmaster/authentication/UserService.java index e904e2e03850f017b55e4b1d4cd22460ee2dbfb9..c854015d7fb819f6c966f9739be13b50724fd138 100644 --- a/src/main/java/de/deadlocker8/budgetmaster/authentication/UserService.java +++ b/src/main/java/de/deadlocker8/budgetmaster/authentication/UserService.java @@ -12,6 +12,7 @@ import org.springframework.stereotype.Service; public class UserService { private final Logger LOGGER = LoggerFactory.getLogger(this.getClass()); + public static final String DEFAULT_PASSWORD = "BudgetMaster"; @Autowired public UserService(UserRepository userRepository, AccountService accountService) @@ -25,7 +26,7 @@ public class UserService if(userRepository.findAll().size() == 0) { BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder(); - String encryptedPassword = bCryptPasswordEncoder.encode("BudgetMaster"); + String encryptedPassword = bCryptPasswordEncoder.encode(DEFAULT_PASSWORD); User user = new User("Default", encryptedPassword); userRepository.save(user); LOGGER.info("Created default user"); diff --git a/src/main/java/de/deadlocker8/budgetmaster/repeating/RepeatingTransactionUpdater.java b/src/main/java/de/deadlocker8/budgetmaster/repeating/RepeatingTransactionUpdater.java index 71e2a47b1f2ad6ccb34c009fe199882be92c5c34..545e0b3ccb3436f37cb4b54158d32a6ff839fc12 100644 --- a/src/main/java/de/deadlocker8/budgetmaster/repeating/RepeatingTransactionUpdater.java +++ b/src/main/java/de/deadlocker8/budgetmaster/repeating/RepeatingTransactionUpdater.java @@ -13,17 +13,15 @@ import java.util.List; @Service public class RepeatingTransactionUpdater { - @Autowired - private TransactionRepository transactionRepository; - - @Autowired - private TransactionService transactionService; + private final TransactionService transactionService; + private final RepeatingOptionRepository repeatingOptionRepository; @Autowired - private RepeatingOptionRepository repeatingOptionRepository; - - @Autowired - private HelpersService helpers; + public RepeatingTransactionUpdater(TransactionService transactionService, RepeatingOptionRepository repeatingOptionRepository) + { + this.transactionService = transactionService; + this.repeatingOptionRepository = repeatingOptionRepository; + } public void updateRepeatingTransactions(DateTime now) { @@ -39,7 +37,7 @@ public class RepeatingTransactionUpdater Transaction newTransaction = new Transaction(transactions.get(0)); newTransaction.setID(null); newTransaction.setDate(currentDate); - transactionRepository.save(newTransaction); + transactionService.getRepository().save(newTransaction); } } } diff --git a/src/main/resources/languages/_de.properties b/src/main/resources/languages/_de.properties index 125681c47e48bf8654fc7dab9cef3dd06a228ffb..92666c96ebe2dcab38c6e6bd5c12bb8f4a2b5c90 100644 --- a/src/main/resources/languages/_de.properties +++ b/src/main/resources/languages/_de.properties @@ -138,7 +138,7 @@ menu.about= menu.logout=Logout menu.accounts=Konten menu.update=Update verf�gbar -menu.search.results=Suchergebnisse +menu.search.results=Suchergebnisse ({0}) category.new.label.name=Name diff --git a/src/main/resources/languages/_en.properties b/src/main/resources/languages/_en.properties index e6fec44e4f297f2b19def9e919150f47a667f1dd..63d3ce0814b920b63f1794e402e438f62b149c2d 100644 --- a/src/main/resources/languages/_en.properties +++ b/src/main/resources/languages/_en.properties @@ -139,7 +139,7 @@ menu.about=About menu.logout=Logout menu.accounts=Accounts menu.update=Update available -menu.search.results=Search results +menu.search.results=Search results ({0}) category.new.label.name=Name diff --git a/src/main/resources/static/css/search.css b/src/main/resources/static/css/search.css index 4085b316171cead3929eccfa4d4a48f80e46e51e..d1d9e63b5189ed472c55e5627667f79711613330 100644 --- a/src/main/resources/static/css/search.css +++ b/src/main/resources/static/css/search.css @@ -2,11 +2,6 @@ background-color: #fafafa; } -.search-checkbox-container input[type="checkbox"] + span:not(.lever)::before, -.search-checkbox-container input[type="checkbox"]:not(.filled-in) + span:not(.lever)::after { - border: 2px solid rgba(255, 255, 255, 0.7); -} - .search-checkbox-container input[type="checkbox"]:checked + span:not(.lever)::before { border-top: 3px solid transparent; border-left: 3px solid transparent; diff --git a/src/main/resources/templates/helpers/header.ftl b/src/main/resources/templates/helpers/header.ftl index dd70a0759daa5fab6acd365ab8a41d7a3af6a9f0..fe828c51d4ae2410e0c92ff0ebdf3cd56479abf8 100644 --- a/src/main/resources/templates/helpers/header.ftl +++ b/src/main/resources/templates/helpers/header.ftl @@ -1,9 +1,9 @@ <#macro style name> <#import "/spring.ftl" as s> <#if helpers.getSettings().isUseDarkTheme()> - <link type="text/css" rel="stylesheet" href=<@s.url '${"/css/dark/" + name + ".css"}'/>/> + <link type="text/css" rel="stylesheet" href="<@s.url '${"/css/dark/" + name + ".css"}'/>"/> <#else> - <link type="text/css" rel="stylesheet" href=<@s.url '${"/css/" + name + ".css"}'/>/> + <link type="text/css" rel="stylesheet" href="<@s.url '${"/css/" + name + ".css"}'/>"/> </#if> </#macro> diff --git a/src/main/resources/templates/helpers/navbar.ftl b/src/main/resources/templates/helpers/navbar.ftl index 6f4c845f3ffa761b7b2467e68e0cae9f680cd7fa..6978277fd0f2d1053c72b66373ce5bcbaac8a70a 100644 --- a/src/main/resources/templates/helpers/navbar.ftl +++ b/src/main/resources/templates/helpers/navbar.ftl @@ -34,6 +34,11 @@ <@itemUpdate "/update", locale.getString("menu.update"), "system_update"/> </#if> + <#if programArgs.isTest()> + <@itemDivider/> + <@itemDebug "TEST MODE" "report_problem"/> + </#if> + <#if programArgs.isDebug()> <@itemDivider/> <@itemDebug "DEBUG MODE" "bug_report"/> @@ -164,7 +169,7 @@ <p>${locale.getString("info.text.backup.reminder")}</p> </div> <div class="modal-footer background-color"> - <a href="<@s.url '/backupReminder/cancel'/>" class="modal-action modal-close waves-effect waves-light red btn-flat white-text">${locale.getString("cancel")}</a> + <a href="<@s.url '/backupReminder/cancel'/>" id="buttonCloseReminder" class="modal-action modal-close waves-effect waves-light red btn-flat white-text">${locale.getString("cancel")}</a> <a href="<@s.url '/backupReminder/settings'/>" class="modal-action modal-close waves-effectwaves-light green btn-flat white-text">${locale.getString("info.button.backup.reminder")}</a> </div> </div> diff --git a/src/main/resources/templates/login.ftl b/src/main/resources/templates/login.ftl index 37122ac9e08fce3e317727ac0cedeab094ea1d8e..e1547d05360e4c401ead497affb2c70e5de07351 100644 --- a/src/main/resources/templates/login.ftl +++ b/src/main/resources/templates/login.ftl @@ -31,7 +31,7 @@ <table class="${redTextColor} login-message no-border-table"> <tr> <td><i class="material-icons">warning</i></td> - <td>${locale.getString("warning.wrong.password")}</td> + <td id="loginMessage">${locale.getString("warning.wrong.password")}</td> </tr> </table> </div> @@ -44,7 +44,7 @@ <table class="${greenTextColor} login-message no-border-table"> <tr> <td><i class="material-icons">info_outline</i></td> - <td>${locale.getString("logout.success")}</td> + <td id="loginMessage">${locale.getString("logout.success")}</td> </tr> </table> </div> diff --git a/src/main/resources/templates/search/search.ftl b/src/main/resources/templates/search/search.ftl index 811acefa9c9228a46025b13918476ebe73c43059..d18cde2b61d89ec7cc69011b177068fb3809e53c 100644 --- a/src/main/resources/templates/search/search.ftl +++ b/src/main/resources/templates/search/search.ftl @@ -18,7 +18,7 @@ <div class="card main-card background-color"> <div class="container"> <div class="section center-align"> - <div class="headline">${locale.getString("menu.search.results")} (${page.getTotalElements()})</div> + <div class="headline">${locale.getString("menu.search.results", page.getTotalElements())}</div> </div> </div> <form id="searchForm" action="<@s.url '/search'/>" method="get"> diff --git a/src/main/resources/templates/settings/import.ftl b/src/main/resources/templates/settings/import.ftl index 53e2568e2740dd1f45b62b26551e9e463ea164d1..119e10474d03f9a529bb1080dbbf273f5d1ad990 100644 --- a/src/main/resources/templates/settings/import.ftl +++ b/src/main/resources/templates/settings/import.ftl @@ -41,7 +41,7 @@ </td> <td class="import-text">${locale.getString("info.database.import.or")}</td> <td> - <a href="<@s.url '/accounts/newAccount'/>" class="btn waves-effect waves-light budgetmaster-blue"><i class="material-icons left">add</i>${locale.getString("title.account.new")}</a> + <a href="<@s.url '/accounts/newAccount'/>" class="btn waves-effect waves-light budgetmaster-blue button-new-account"><i class="material-icons left">add</i>${locale.getString("title.account.new")}</a> </td> </tr> </#list> @@ -61,7 +61,7 @@ </div> <div class="col m6 l4 left-align"> - <button class="btn waves-effect waves-light budgetmaster-blue" type="submit" name="action"> + <button class="btn waves-effect waves-light budgetmaster-blue" type="submit" name="action" id="buttonImport"> <i class="material-icons left">unarchive</i>${locale.getString("settings.database.import")} </button> </div> diff --git a/src/main/resources/templates/settings/settingsMacros.ftl b/src/main/resources/templates/settings/settingsMacros.ftl index 550e6212bedf45cbb2a077f9beb7c87426e4a657..816778c61648fe4e372739e2419fa80e002a4c4b 100644 --- a/src/main/resources/templates/settings/settingsMacros.ftl +++ b/src/main/resources/templates/settings/settingsMacros.ftl @@ -100,7 +100,7 @@ <div class="file-field input-field"> <div class="btn budgetmaster-blue"> <i class="material-icons">cloud_upload</i> - <input type="file" accept=".json" name="file"> + <input id="inputDatabaseImport" type="file" accept=".json" name="file"> </div> <div class="file-path-wrapper"> <input class="file-path validate" type="text"> diff --git a/src/main/resources/templates/transactions/transactionsMacros.ftl b/src/main/resources/templates/transactions/transactionsMacros.ftl index bcbad2bbc2edc1da4b6c0450bc9f884b0fcfa7cc..9a6ce3a29ecb96643ffee62e9a60d082866aade2 100644 --- a/src/main/resources/templates/transactions/transactionsMacros.ftl +++ b/src/main/resources/templates/transactions/transactionsMacros.ftl @@ -68,7 +68,7 @@ <#macro transactionLinks transaction> <div class="col s4 l2 xl1 right-align transaction-buttons no-wrap"> - <a href="<@s.url '/transactions/${transaction.ID?c}/highlight'/>" class="btn-flat no-padding text-color"><i class="material-icons left">open_in_new</i></a> + <a href="<@s.url '/transactions/${transaction.ID?c}/highlight'/>" class="btn-flat no-padding text-color buttonHighlight"><i class="material-icons left">open_in_new</i></a> <a href="<@s.url '/transactions/${transaction.ID?c}/edit'/>" class="btn-flat no-padding text-color"><i class="material-icons left no-margin">edit</i></a> </div> </#macro> diff --git a/src/test/java/de/deadlocker8/budgetmaster/integration/ImportTest.java b/src/test/java/de/deadlocker8/budgetmaster/integration/ImportTest.java new file mode 100644 index 0000000000000000000000000000000000000000..3722006bc6d646e8267b2be293ea6e83ca12e337 --- /dev/null +++ b/src/test/java/de/deadlocker8/budgetmaster/integration/ImportTest.java @@ -0,0 +1,44 @@ +package de.deadlocker8.budgetmaster.integration; + +import de.deadlocker8.budgetmaster.Main; +import de.deadlocker8.budgetmaster.authentication.UserService; +import de.deadlocker8.budgetmaster.integration.helpers.IntegrationTestHelper; +import de.deadlocker8.budgetmaster.integration.helpers.SeleniumTest; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.openqa.selenium.WebDriver; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.context.embedded.LocalServerPort; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +import java.io.File; +import java.util.Arrays; +import java.util.List; + +@RunWith(SpringJUnit4ClassRunner.class) +@SpringBootTest(classes = Main.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +@SeleniumTest +public class ImportTest +{ + @SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection") + @Autowired + private WebDriver driver; + + @LocalServerPort + int port; + + @Test + public void requestImport() + { + IntegrationTestHelper helper = new IntegrationTestHelper(driver, port); + helper.start(); + helper.login(UserService.DEFAULT_PASSWORD); + helper.hideBackupReminder(); + + String path = getClass().getClassLoader().getResource("SearchDatabase.json").getFile().replace("/", File.separator); + List<String> sourceAccounts = Arrays.asList("DefaultAccount0815", "sfsdf"); + List<String> destinationAccounts = Arrays.asList("DefaultAccount0815", "Account2"); + helper.uploadDatabase(path, sourceAccounts, destinationAccounts); + } +} \ No newline at end of file diff --git a/src/test/java/de/deadlocker8/budgetmaster/integration/IntegrationTestHelper.java b/src/test/java/de/deadlocker8/budgetmaster/integration/IntegrationTestHelper.java deleted file mode 100644 index 61b74c81ec16f3a147d59806bf0e39aea72517a6..0000000000000000000000000000000000000000 --- a/src/test/java/de/deadlocker8/budgetmaster/integration/IntegrationTestHelper.java +++ /dev/null @@ -1,20 +0,0 @@ -package de.deadlocker8.budgetmaster.integration; - -import org.openqa.selenium.By; -import org.openqa.selenium.WebElement; - -import java.util.List; - -public class IntegrationTestHelper -{ - public static String getTextNode(WebElement e) - { - String text = e.getText().trim(); - List<WebElement> children = e.findElements(By.xpath("./*")); - for(WebElement child : children) - { - text = text.replaceFirst(child.getText(), "").trim(); - } - return text; - } -} diff --git a/src/test/java/de/deadlocker8/budgetmaster/integration/LoginControllerTest.java b/src/test/java/de/deadlocker8/budgetmaster/integration/LoginControllerTest.java index e7739548e08df462981f534171874d41153f3852..1b4c17f45b01c613698ab12ee2e78bb8570ed813 100644 --- a/src/test/java/de/deadlocker8/budgetmaster/integration/LoginControllerTest.java +++ b/src/test/java/de/deadlocker8/budgetmaster/integration/LoginControllerTest.java @@ -1,8 +1,10 @@ package de.deadlocker8.budgetmaster.integration; import de.deadlocker8.budgetmaster.Main; +import de.deadlocker8.budgetmaster.authentication.UserService; +import de.deadlocker8.budgetmaster.integration.helpers.IntegrationTestHelper; +import de.deadlocker8.budgetmaster.integration.helpers.SeleniumTest; import de.thecodelabs.utils.util.Localization; -import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.openqa.selenium.By; @@ -17,29 +19,23 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; @RunWith(SpringJUnit4ClassRunner.class) -@SpringBootTest(classes = Main.class,webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +@SpringBootTest(classes = Main.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) @SeleniumTest public class LoginControllerTest { @SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection") @Autowired private WebDriver driver; - private final static String BASE_URL = "https://localhost:"; - private String url; @LocalServerPort int port; - @Before - public void before() - { - url = BASE_URL + port; - } - @Test public void getLoginPage() { - driver.get(url); + IntegrationTestHelper helper = new IntegrationTestHelper(driver, port); + helper.start(); + WebElement input = driver.findElement(By.id("login-password")); assertNotNull(input); @@ -49,4 +45,41 @@ public class LoginControllerTest WebElement button = driver.findElement(By.tagName("button")); assertEquals(Localization.getString("login.button"), IntegrationTestHelper.getTextNode(button)); } + + @Test + public void wrongCredentials() + { + IntegrationTestHelper helper = new IntegrationTestHelper(driver, port); + helper.start(); + helper.login("akhjfvbvahsdsa"); + + WebElement label = driver.findElement(By.id("loginMessage")); + assertEquals(Localization.getString("warning.wrong.password"), label.getText()); + } + + @Test + public void successLogin() + { + IntegrationTestHelper helper = new IntegrationTestHelper(driver, port); + helper.start(); + helper.login(UserService.DEFAULT_PASSWORD); + + WebElement label = driver.findElement(By.id("logo-home")); + String expected = helper.getUrl() + "/images/Logo_with_text_medium_res.png"; + assertEquals(expected, label.getAttribute("src")); + } + + @Test + public void logout() + { + IntegrationTestHelper helper = new IntegrationTestHelper(driver, port); + helper.start(); + helper.login(UserService.DEFAULT_PASSWORD); + + WebElement buttonLogout = driver.findElement(By.xpath("//body/ul/li/a[contains(text(), 'Logout')]")); + buttonLogout.click(); + + WebElement label = driver.findElement(By.id("loginMessage")); + assertEquals(Localization.getString("logout.success"), label.getText()); + } } \ No newline at end of file diff --git a/src/test/java/de/deadlocker8/budgetmaster/integration/SearchTest.java b/src/test/java/de/deadlocker8/budgetmaster/integration/SearchTest.java new file mode 100644 index 0000000000000000000000000000000000000000..f8fe97108630750308e5de45983804d191e88add --- /dev/null +++ b/src/test/java/de/deadlocker8/budgetmaster/integration/SearchTest.java @@ -0,0 +1,156 @@ +package de.deadlocker8.budgetmaster.integration; + +import de.deadlocker8.budgetmaster.Main; +import de.deadlocker8.budgetmaster.authentication.UserService; +import de.deadlocker8.budgetmaster.integration.helpers.IntegrationTestHelper; +import de.deadlocker8.budgetmaster.integration.helpers.SeleniumTest; +import de.thecodelabs.utils.util.Localization; +import org.junit.Before; +import org.junit.Test; +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.support.ui.ExpectedConditions; +import org.openqa.selenium.support.ui.WebDriverWait; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.context.embedded.LocalServerPort; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +import java.io.File; +import java.util.Arrays; +import java.util.List; + +import static junit.framework.TestCase.assertTrue; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; + +@RunWith(SpringJUnit4ClassRunner.class) +@SpringBootTest(classes = Main.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +@SeleniumTest +public class SearchTest +{ + @SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection") + @Autowired + private WebDriver driver; + + @LocalServerPort + int port; + + @Before + public void prepare() + { + // prepare + IntegrationTestHelper helper = new IntegrationTestHelper(driver, port); + helper.start(); + helper.login(UserService.DEFAULT_PASSWORD); + helper.hideBackupReminder(); + + String path = getClass().getClassLoader().getResource("SearchDatabase.json").getFile().replace("/", File.separator); + helper.uploadDatabase(path, Arrays.asList("DefaultAccount0815", "sfsdf"), Arrays.asList("DefaultAccount0815", "Account2")); + + // search + WebElement inputSearch = driver.findElement(By.id("search")); + inputSearch.sendKeys("e"); + driver.findElement(By.id("buttonSearch")).click(); + } + + @Test + public void searchFromNavbar() + { + // headline + WebElement headline = driver.findElement(By.className("headline")); + String expected = Localization.getString("menu.search.results", 24); + assertEquals(expected, headline.getText()); + + // checkboxes + assertTrue(driver.findElement(By.cssSelector(".main-card #searchForm input[name=\"searchName\"]")).isSelected()); + assertTrue(driver.findElement(By.cssSelector(".main-card #searchForm input[name=\"searchDescription\"]")).isSelected()); + assertTrue(driver.findElement(By.cssSelector(".main-card #searchForm input[name=\"searchCategory\"]")).isSelected()); + assertTrue(driver.findElement(By.cssSelector(".main-card #searchForm input[name=\"searchTags\"]")).isSelected()); + + // results + List<WebElement> results = driver.findElements(By.cssSelector(".search-container .card-panel")); + assertEquals(10, results.size()); + } + + @Test + public void pagination() + { + // === PAGE 1 === + List<WebElement> pages = driver.findElements(By.cssSelector(".pagination li")); + assertEquals(5, pages.size()); + + assertTrue(pages.get(0).getAttribute("class").contains("disabled")); + assertEquals("1", pages.get(1).findElement(By.className("page-link")).getText()); + assertTrue(pages.get(1).getAttribute("class").contains("active")); + assertEquals("2", pages.get(2).findElement(By.className("page-link")).getText()); + assertEquals("3", pages.get(3).findElement(By.className("page-link")).getText()); + assertTrue(!pages.get(4).getAttribute("class").contains("disabled")); + + // validate results + List<WebElement> results = driver.findElements(By.cssSelector(".search-container .card-panel")); + assertEquals(10, results.size()); + + // === PAGE 1 === + pages.get(3).click(); + + pages = driver.findElements(By.cssSelector(".pagination li")); + assertEquals(5, pages.size()); + + // previous button should be enabled + assertTrue(!pages.get(0).getAttribute("class").contains("disabled")); + + assertEquals("1", pages.get(1).findElement(By.className("page-link")).getText()); + assertEquals("2", pages.get(2).findElement(By.className("page-link")).getText()); + assertEquals("3", pages.get(3).findElement(By.className("page-link")).getText()); + assertTrue(pages.get(3).getAttribute("class").contains("active")); + + // next button should be disabled + assertTrue(pages.get(4).getAttribute("class").contains("disabled")); + + // validate + results = driver.findElements(By.cssSelector(".search-container .card-panel")); + assertEquals(4, results.size()); + } + + @Test + public void checkboxes() + { + // deselect some checkboxes (use JavascriptExecutor here as the checkbox is covered by a span) + JavascriptExecutor executor = (JavascriptExecutor) driver; + executor.executeScript("arguments[0].click();", driver.findElement(By.cssSelector(".main-card #searchForm input[name=\"searchName\"]"))); + executor.executeScript("arguments[0].click();", driver.findElement(By.cssSelector(".main-card #searchForm input[name=\"searchDescription\"]"))); + + // search + driver.findElement(By.cssSelector(".main-card #searchForm button[type=\"submit\"]")).click(); + + // validate + assertFalse(driver.findElement(By.cssSelector(".main-card #searchForm input[name=\"searchName\"]")).isSelected()); + assertFalse(driver.findElement(By.cssSelector(".main-card #searchForm input[name=\"searchDescription\"]")).isSelected()); + assertTrue(driver.findElement(By.cssSelector(".main-card #searchForm input[name=\"searchCategory\"]")).isSelected()); + assertTrue(driver.findElement(By.cssSelector(".main-card #searchForm input[name=\"searchTags\"]")).isSelected()); + + // results + List<WebElement> results = driver.findElements(By.cssSelector(".search-container .card-panel")); + assertEquals(2, results.size()); + } + + @Test + public void highlight() + { + driver.findElement(By.cssSelector(".main-card .search-result .hide-on-med-and-down .buttonHighlight")).click(); + + assertEquals("May 2019", driver.findElement(By.cssSelector(".headline-date")).getText()); + + List<WebElement> transactionsRows = driver.findElements(By.cssSelector(".transaction-container .hide-on-med-and-down.transaction-row-top")); + assertEquals(25, transactionsRows.size()); + assertTrue(transactionsRows.get(0).getAttribute("class").contains("budgetmaster-blue-light")); + for(int i = 1; i < transactionsRows.size(); i++) + { + assertFalse(transactionsRows.get(i).getAttribute("class").contains("budgetmaster-blue-light")); + } + } +} \ No newline at end of file diff --git a/src/test/java/de/deadlocker8/budgetmaster/integration/helpers/IntegrationTestHelper.java b/src/test/java/de/deadlocker8/budgetmaster/integration/helpers/IntegrationTestHelper.java new file mode 100644 index 0000000000000000000000000000000000000000..66d440e9c8a09c950236350ccae6a91bd2e5dc28 --- /dev/null +++ b/src/test/java/de/deadlocker8/budgetmaster/integration/helpers/IntegrationTestHelper.java @@ -0,0 +1,130 @@ +package de.deadlocker8.budgetmaster.integration.helpers; + +import de.thecodelabs.utils.util.Localization; +import org.openqa.selenium.By; +import org.openqa.selenium.NoSuchElementException; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebElement; + +import java.util.List; + +import static org.junit.Assert.assertEquals; + +public class IntegrationTestHelper +{ + public static String getTextNode(WebElement e) + { + String text = e.getText().trim(); + List<WebElement> children = e.findElements(By.xpath("./*")); + for(WebElement child : children) + { + text = text.replaceFirst(child.getText(), "").trim(); + } + return text; + } + + private final static String BASE_URL = "https://localhost:"; + private WebDriver driver; + private String url; + + public IntegrationTestHelper(WebDriver driver, int port) + { + this.driver = driver; + this.url = BASE_URL + port; + } + + public String getUrl() + { + return url; + } + + public void start() + { + driver.get(url); + } + + public void login(String password) + { + WebElement inputPassword = driver.findElement(By.id("login-password")); + inputPassword.sendKeys(password); + WebElement buttonLogin = driver.findElement(By.tagName("button")); + buttonLogin.click(); + } + + public void hideBackupReminder() + { + try + { + WebElement buttonCloseReminder = driver.findElement(By.cssSelector("#modalBackupReminder #buttonCloseReminder")); + buttonCloseReminder.click(); + } + catch(NoSuchElementException ignored) + { + } + } + + public void uploadDatabase(String path, List<String> sourceAccounts, List<String> destinationAccounts) + { + if(path.startsWith("\\")) + { + path = path.substring(1); + } + + driver.get(url + "/settings/database/requestImport"); + + // upload database + WebElement input = driver.findElement(By.id("inputDatabaseImport")); + input.sendKeys(path); + + // confirm upload + driver.findElement(By.id("button-confirm-database-import")).click(); + + // create new accounts + for(String account : destinationAccounts) + { + createAccountOnImport(account); + } + + // account matching + matchAccounts(sourceAccounts, destinationAccounts); + + // confirm import + driver.findElement(By.id("buttonImport")).click(); + + assertEquals(Localization.getString("menu.settings"), IntegrationTestHelper.getTextNode(driver.findElement(By.className("headline")))); + + // open transactions page in order to update repeating transactions + driver.get(url + "/transactions"); + start(); + } + + private void createAccountOnImport(String accountName) + { + driver.findElement(By.className("button-new-account")).click(); + WebElement inputAccountName = driver.findElement(By.id("account-name")); + inputAccountName.sendKeys(accountName); + driver.findElement(By.tagName("button")).click(); + } + + private void matchAccounts(List<String> sourceAccounts, List<String> destinationAccounts) + { + WebElement headlineImport = driver.findElement(By.className("headline")); + assertEquals(Localization.getString("info.title.database.import.dialog"), IntegrationTestHelper.getTextNode(headlineImport)); + + List<WebElement> tableRows = driver.findElements(By.cssSelector(".container form table tr")); + assertEquals(destinationAccounts.size(), tableRows.size()); + + for(int i = 0; i < destinationAccounts.size(); i++) + { + String account = destinationAccounts.get(i); + + WebElement row = tableRows.get(i); + WebElement sourceAccount = row.findElement(By.className("account-source")); + assertEquals(sourceAccounts.get(i), IntegrationTestHelper.getTextNode(sourceAccount)); + + row.findElement(By.className("select-dropdown")).click(); + WebElement accountToSelect = row.findElement(By.xpath("//form/table/tbody/tr[" + (i+1) + "]/td[5]/div/div/ul/li/span[text()='" + account + "']")); + accountToSelect.click(); + } + } +} diff --git a/src/test/java/de/deadlocker8/budgetmaster/integration/SeleniumTest.java b/src/test/java/de/deadlocker8/budgetmaster/integration/helpers/SeleniumTest.java similarity index 88% rename from src/test/java/de/deadlocker8/budgetmaster/integration/SeleniumTest.java rename to src/test/java/de/deadlocker8/budgetmaster/integration/helpers/SeleniumTest.java index 10f8f4f75b59e0e5f2d84a74e26bb9811e80d685..e45b75de42b2cfcc54ea559db521ba0be5e0dfb2 100644 --- a/src/test/java/de/deadlocker8/budgetmaster/integration/SeleniumTest.java +++ b/src/test/java/de/deadlocker8/budgetmaster/integration/helpers/SeleniumTest.java @@ -1,4 +1,4 @@ -package de.deadlocker8.budgetmaster.integration; +package de.deadlocker8.budgetmaster.integration.helpers; import org.springframework.test.context.TestExecutionListeners; diff --git a/src/test/java/de/deadlocker8/budgetmaster/integration/SeleniumTestExecutionListener.java b/src/test/java/de/deadlocker8/budgetmaster/integration/helpers/SeleniumTestExecutionListener.java similarity index 90% rename from src/test/java/de/deadlocker8/budgetmaster/integration/SeleniumTestExecutionListener.java rename to src/test/java/de/deadlocker8/budgetmaster/integration/helpers/SeleniumTestExecutionListener.java index 34aac3cbef3a312c448466f97477dfda14a09fbe..3f6f98f5e4f100bee2ca90fd7a61f899c26ea41d 100644 --- a/src/test/java/de/deadlocker8/budgetmaster/integration/SeleniumTestExecutionListener.java +++ b/src/test/java/de/deadlocker8/budgetmaster/integration/helpers/SeleniumTestExecutionListener.java @@ -1,4 +1,4 @@ -package de.deadlocker8.budgetmaster.integration; +package de.deadlocker8.budgetmaster.integration.helpers; import org.openqa.selenium.OutputType; import org.openqa.selenium.TakesScreenshot; @@ -31,6 +31,11 @@ public class SeleniumTestExecutionListener extends AbstractTestExecutionListener @Override public void prepareTestInstance(TestContext testContext) { + if(!System.getProperties().containsKey("testProfile")) + { + throw new RuntimeException("Test profile not activated. Skipping tests. (Set -DtestProfile=true in your VM arguments)"); + } + if(webDriver != null) { return; diff --git a/src/test/resources/SearchDatabase.json b/src/test/resources/SearchDatabase.json new file mode 100644 index 0000000000000000000000000000000000000000..cb16a6be6465fae89a5ffdb56b7c57dc794b073c --- /dev/null +++ b/src/test/resources/SearchDatabase.json @@ -0,0 +1,168 @@ +{ + "TYPE": "BUDGETMASTER_DATABASE", + "VERSION": 3, + "categories": [ + { + "ID": 1, + "name": "No Category", + "color": "#FFFFFF", + "type": "NONE" + }, + { + "ID": 2, + "name": "Rest", + "color": "#FFFF00", + "type": "REST" + }, + { + "ID": 97, + "name": "sdfdsf", + "color": "#2e7c2b", + "type": "CUSTOM" + }, + { + "ID": 98, + "name": "12sd", + "color": "#ff3b30", + "type": "CUSTOM" + } + ], + "accounts": [ + { + "ID": 1, + "name": "Placeholder", + "type": "ALL" + }, + { + "ID": 2, + "name": "DefaultAccount0815", + "type": "CUSTOM" + }, + { + "ID": 3, + "name": "sfsdf", + "type": "CUSTOM" + } + ], + "transactions": [ + { + "ID": 97, + "amount": -20000, + "date": "2019-04-24", + "account": { + "ID": 3, + "name": "sfsdf", + "type": "CUSTOM" + }, + "category": { + "ID": 1, + "name": "No Category", + "color": "#FFFFFF", + "type": "NONE" + }, + "name": "Umbuchung", + "description": "", + "tags": [], + "transferAccount": { + "ID": 2, + "name": "DefaultAccount0815", + "type": "CUSTOM" + } + }, + { + "ID": 99, + "amount": -1500, + "date": "2019-05-01", + "account": { + "ID": 2, + "name": "DefaultAccount0815", + "type": "CUSTOM" + }, + "category": { + "ID": 98, + "name": "12sd", + "color": "#ff3b30", + "type": "CUSTOM" + }, + "name": "Test", + "description": "", + "tags": [] + }, + { + "ID": 195, + "amount": -300, + "date": "2019-05-01", + "account": { + "ID": 2, + "name": "DefaultAccount0815", + "type": "CUSTOM" + }, + "category": { + "ID": 1, + "name": "No Category", + "color": "#FFFFFF", + "type": "NONE" + }, + "name": "Transfer dings", + "description": "", + "tags": [], + "transferAccount": { + "ID": 3, + "name": "sfsdf", + "type": "CUSTOM" + } + }, + { + "ID": 199, + "amount": 100, + "date": "2019-05-01", + "account": { + "ID": 2, + "name": "DefaultAccount0815", + "type": "CUSTOM" + }, + "category": { + "ID": 98, + "name": "12sd", + "color": "#ff3b30", + "type": "CUSTOM" + }, + "name": "xxx", + "description": "", + "tags": [] + }, + { + "ID": 227, + "amount": -1500, + "date": "2019-05-01", + "account": { + "ID": 2, + "name": "DefaultAccount0815", + "type": "CUSTOM" + }, + "category": { + "ID": 97, + "name": "sdfdsf", + "color": "#2e7c2b", + "type": "CUSTOM" + }, + "name": "beste", + "description": "Lorem Ipsum", + "tags": [], + "repeatingOption": { + "ID": 161, + "startDate": "2019-05-01", + "modifier": { + "ID": 161, + "quantity": 1, + "localizationKey": "repeating.modifier.days" + }, + "endOption": { + "times": 20, + "ID": 161, + "localizationKey": "repeating.end.key.afterXTimes" + } + } + } + ] +} \ No newline at end of file