diff --git a/Dockerfile b/Dockerfile index 9b44a1de941c62bfe1a6727010266eddb8a0b783..128d5b9ce131b68044d714774c2b20aae677a5ee 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,7 @@ FROM tomcat:9-jre8 RUN rm -rf /usr/local/tomcat/webapps/* -COPY build/2.4.4/BudgetMaster-v2.4.4.war $CATALINA_HOME/webapps/ROOT.war +COPY build/2.4.5/BudgetMaster-v2.4.5.war $CATALINA_HOME/webapps/ROOT.war COPY src/main/resources/config/templates/settings-docker.properties /root/.Deadlocker/BudgetMaster/settings.properties EXPOSE 8080 \ No newline at end of file diff --git a/README.md b/README.md index cf346f5e123db649d33c294e94c3d15c32cc0f12..0ce08f5c9407edfdf3311ba14fb0cc1dec2bdac0 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ Manage your monthly budget easily with BudgetMaster - __start:__ 17.12.16 -- __current release:__ v2.4.4 (27) from 05.08.20 +- __current release:__ v2.4.5 (28) from 19.08.20 ## Key Features - Keep your data private - Host your own BudgetMaster server or use it in standalone mode. All data remains on your machines. diff --git a/pom.xml b/pom.xml index 2127f964352288855b8608de139fe07ca703166e..9f82d2adf5474754b08897bc7078f0d52579332c 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ <groupId>de.deadlocker8</groupId> <artifactId>BudgetMaster</artifactId> - <version>2.4.4</version> + <version>2.4.5</version> <name>BudgetMaster</name> <repositories> @@ -70,7 +70,7 @@ <app.versionDate>${maven.build.timestamp}</app.versionDate> <maven.build.timestamp.format>dd.MM.yy</maven.build.timestamp.format> - <app.versionCode>27</app.versionCode> + <app.versionCode>28</app.versionCode> <app.author>Robert Goldmann</app.author> <project.outputDirectory>build/${project.version}</project.outputDirectory> diff --git a/src/main/java/de/deadlocker8/budgetmaster/templates/Template.java b/src/main/java/de/deadlocker8/budgetmaster/templates/Template.java index 7eaa176fe4a6772eabd61e1eb373ab58e24774b7..e0b58d35a4165121567c1d5a43f2c1e43d14e04d 100644 --- a/src/main/java/de/deadlocker8/budgetmaster/templates/Template.java +++ b/src/main/java/de/deadlocker8/budgetmaster/templates/Template.java @@ -139,7 +139,7 @@ public class Template implements TransactionBase return isExpenditure; } - public void setExpenditure(Boolean expenditure) + public void setIsExpenditure(Boolean expenditure) { isExpenditure = expenditure; } diff --git a/src/main/java/de/deadlocker8/budgetmaster/templates/TemplateController.java b/src/main/java/de/deadlocker8/budgetmaster/templates/TemplateController.java index 972a4e6ccbd640442c9dec5b34a6fb76af194ded..1f7a484fbf1903666dfbf56137b03a44f81e333a 100644 --- a/src/main/java/de/deadlocker8/budgetmaster/templates/TemplateController.java +++ b/src/main/java/de/deadlocker8/budgetmaster/templates/TemplateController.java @@ -129,7 +129,7 @@ public class TemplateController extends BaseController if(template.getAmount() == null) { - template.setExpenditure(true); + template.setIsExpenditure(true); } final DateTime date = dateService.getDateTimeFromCookie(cookieDate); @@ -175,7 +175,7 @@ public class TemplateController extends BaseController if(template.isExpenditure() == null) { - template.setExpenditure(false); + template.setIsExpenditure(true); } if(template.getAmount() != null) diff --git a/src/main/java/de/deadlocker8/budgetmaster/transactions/Transaction.java b/src/main/java/de/deadlocker8/budgetmaster/transactions/Transaction.java index 0c950ba7ca5b989966daf4088f674dab3ecf80ce..af010b146f88b88f2398f25fa03ac94949e9a363 100644 --- a/src/main/java/de/deadlocker8/budgetmaster/transactions/Transaction.java +++ b/src/main/java/de/deadlocker8/budgetmaster/transactions/Transaction.java @@ -111,7 +111,7 @@ public class Transaction implements TransactionBase return isExpenditure; } - public void setExpenditure(Boolean expenditure) + public void setIsExpenditure(Boolean expenditure) { isExpenditure = expenditure; } diff --git a/src/main/java/de/deadlocker8/budgetmaster/transactions/TransactionBase.java b/src/main/java/de/deadlocker8/budgetmaster/transactions/TransactionBase.java index 06b7bdb74d60bcc8710751328c5d881bec2d494c..5589d577ffc1df5b1ef8aa7282837c6e589e53ba 100644 --- a/src/main/java/de/deadlocker8/budgetmaster/transactions/TransactionBase.java +++ b/src/main/java/de/deadlocker8/budgetmaster/transactions/TransactionBase.java @@ -14,7 +14,7 @@ public interface TransactionBase Boolean isExpenditure(); - void setExpenditure(Boolean isExpenditure); + void setIsExpenditure(Boolean isExpenditure); Category getCategory(); diff --git a/src/main/java/de/deadlocker8/budgetmaster/transactions/TransactionService.java b/src/main/java/de/deadlocker8/budgetmaster/transactions/TransactionService.java index 6b4fd35b3e0a1a5594c9c8824af1c3c44e57bfb6..969d49b94f410ac5a4f87e93999e20ebaf5acf88 100644 --- a/src/main/java/de/deadlocker8/budgetmaster/transactions/TransactionService.java +++ b/src/main/java/de/deadlocker8/budgetmaster/transactions/TransactionService.java @@ -225,7 +225,7 @@ public class TransactionService implements Resetable { if(item.isExpenditure() == null) { - item.setExpenditure(false); + item.setIsExpenditure(true); } if(item.getAmount() == null) diff --git a/src/main/java/de/deadlocker8/budgetmaster/utils/eventlistener/IntroduceIsExpenditureMember.java b/src/main/java/de/deadlocker8/budgetmaster/utils/eventlistener/IntroduceIsExpenditureMember.java index bd0c0f8b170e03ebd7e1a9abc70bcbf3c90f19d4..f037631cc0c9410935dbe877b8b8d14aeb0f30a4 100644 --- a/src/main/java/de/deadlocker8/budgetmaster/utils/eventlistener/IntroduceIsExpenditureMember.java +++ b/src/main/java/de/deadlocker8/budgetmaster/utils/eventlistener/IntroduceIsExpenditureMember.java @@ -55,7 +55,7 @@ public class IntroduceIsExpenditureMember continue; } - transaction.setExpenditure(transaction.getAmount() <= 0); + transaction.setIsExpenditure(transaction.getAmount() <= 0); fixedTransactionsCount++; } diff --git a/src/main/resources/languages/_de.properties b/src/main/resources/languages/_de.properties index 3725fd521a5c9bb6a4e3fa7027f43ea2ca4ed940..86714465b268382e60d1a7d518f168c765c021b6 100644 --- a/src/main/resources/languages/_de.properties +++ b/src/main/resources/languages/_de.properties @@ -346,7 +346,7 @@ home.menu.settings.action.manage=Einstellungen # hotkeys hotkeys.transactions.new.normal=Neue Buchung anlegen hotkeys.transactions.new.normal.key=n -hotkeys.transactions.new.repeating=Neue widerholende Buchung anlegen +hotkeys.transactions.new.repeating=Neue wiederholende Buchung anlegen hotkeys.transactions.new.repeating.key=r hotkeys.transactions.new.transfer=Neue Umbuchung anlegen hotkeys.transactions.new.transfer.key=t diff --git a/src/main/resources/templates/templates/newTemplate.ftl b/src/main/resources/templates/templates/newTemplate.ftl index f266971652caf1c682cf0442f8c87eba063a3e39..42fea8906260cb7c6cf57444d34959bf17420157 100644 --- a/src/main/resources/templates/templates/newTemplate.ftl +++ b/src/main/resources/templates/templates/newTemplate.ftl @@ -10,7 +10,7 @@ </head> <body class="budgetmaster-blue-light"> <#import "../helpers/navbar.ftl" as navbar> - <@navbar.navbar "transactions" settings/> + <@navbar.navbar "templates" settings/> <#import "../transactions/newTransactionMacros.ftl" as newTransactionMacros> <#import "templateFunctions.ftl" as templateFunctions> @@ -69,7 +69,7 @@ <br> <#-- buttons --> - <@newTransactionMacros.buttons/> + <@newTransactionMacros.buttons "/templates"/> </form> </div> </div> diff --git a/src/main/resources/templates/transactions/newTransactionMacros.ftl b/src/main/resources/templates/transactions/newTransactionMacros.ftl index bc9518ab61d14a0add786b918d20080ad8ada55f..731f3c2bf52c05e4200df6af8ca6c827d2ac5682 100644 --- a/src/main/resources/templates/transactions/newTransactionMacros.ftl +++ b/src/main/resources/templates/transactions/newTransactionMacros.ftl @@ -340,10 +340,10 @@ </div> </#macro> -<#macro buttons> +<#macro buttons cancelURL> <div class="row hide-on-small-only"> <div class="col s6 right-align"> - <@buttonCancel/> + <@buttonCancel cancelURL/> </div> <div class="col s6 left-align"> @@ -354,7 +354,7 @@ <div class="hide-on-med-and-up"> <div class="row center-align"> <div class="col s12"> - <@buttonCancel/> + <@buttonCancel cancelURL/> </div> </div> <div class="row center-align"> @@ -365,8 +365,8 @@ </div> </#macro> -<#macro buttonCancel> - <a href="<@s.url '/transactions'/>" class="waves-effect waves-light btn budgetmaster-blue"><i class="material-icons left">clear</i>${locale.getString("cancel")}</a> +<#macro buttonCancel cancelURL> + <a href="<@s.url cancelURL/>" class="waves-effect waves-light btn budgetmaster-blue"><i class="material-icons left">clear</i>${locale.getString("cancel")}</a> </#macro> <#macro buttonSave> diff --git a/src/main/resources/templates/transactions/newTransactionNormal.ftl b/src/main/resources/templates/transactions/newTransactionNormal.ftl index fc4e34daed42d4a226be613e30a03fa57648c298..c9ff04c77e8cbed71e894e8e5e9ba5b090e514dd 100644 --- a/src/main/resources/templates/transactions/newTransactionNormal.ftl +++ b/src/main/resources/templates/transactions/newTransactionNormal.ftl @@ -60,7 +60,7 @@ <br> <#-- buttons --> - <@newTransactionMacros.buttons/> + <@newTransactionMacros.buttons "/transactions"/> <@newTransactionMacros.buttonTemplate/> </form> diff --git a/src/main/resources/templates/transactions/newTransactionRepeating.ftl b/src/main/resources/templates/transactions/newTransactionRepeating.ftl index 6c30e5862eaab20e08fe677c808a542d12359e22..0362a0c29e709d66f5917d909f5b3271281539f0 100644 --- a/src/main/resources/templates/transactions/newTransactionRepeating.ftl +++ b/src/main/resources/templates/transactions/newTransactionRepeating.ftl @@ -60,7 +60,7 @@ <@newTransactionMacros.transactionRepeating transaction currentDate/> <br> <#-- buttons --> - <@newTransactionMacros.buttons/> + <@newTransactionMacros.buttons "/transactions"/> </form> </div> </div> diff --git a/src/main/resources/templates/transactions/newTransactionTransfer.ftl b/src/main/resources/templates/transactions/newTransactionTransfer.ftl index a4bbc948d2b0d1c91e0122694a7f7f56fa7cd7b7..7eeb0e0336b7da9f9c4d6926d783cc30882734b5 100644 --- a/src/main/resources/templates/transactions/newTransactionTransfer.ftl +++ b/src/main/resources/templates/transactions/newTransactionTransfer.ftl @@ -65,7 +65,7 @@ <br> <#-- buttons --> - <@newTransactionMacros.buttons/> + <@newTransactionMacros.buttons "/transactions"/> <@newTransactionMacros.buttonTemplate/> </form> diff --git a/src/test/java/de/deadlocker8/budgetmaster/integration/NewTransactionTest.java b/src/test/java/de/deadlocker8/budgetmaster/integration/NewTransactionTest.java new file mode 100644 index 0000000000000000000000000000000000000000..e9c89dc78096dba079a7f5ac998b98c717229f87 --- /dev/null +++ b/src/test/java/de/deadlocker8/budgetmaster/integration/NewTransactionTest.java @@ -0,0 +1,205 @@ +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.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.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.text.SimpleDateFormat; +import java.util.Arrays; +import java.util.Date; +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 NewTransactionTest +{ + 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, NewTransactionTest.class); + } + }; + + @Before + public void prepare() + { + FirefoxOptions options = new FirefoxOptions(); + options.setHeadless(true); + driver = new FirefoxDriver(options); + + // prepare + 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")); + + // open transactions page + driver.get(helper.getUrl() + "/transactions"); + driver.findElement(By.id("button-new-transaction")).click(); + } + + @Test + public void newTransaction_normal_cancel() + { + // open new transaction page + driver.findElement(By.xpath("//div[contains(@class, 'new-transaction-button')]//a[contains(text(),'Transaction')]")).click(); + + // click cancel button + driver.findElement(By.xpath("//a[contains(text(),'Cancel')]")).click(); + + WebDriverWait wait = new WebDriverWait(driver, 5); + wait.until(ExpectedConditions.presenceOfElementLocated(By.cssSelector(".headline-date"))); + + // assert + assertThat(driver.getCurrentUrl()).endsWith("/transactions"); + + List<WebElement> transactionsRows = driver.findElements(By.cssSelector(".transaction-container .hide-on-med-and-down.transaction-row-top")); + assertThat(transactionsRows).hasSize(1); + } + + @Test + public void newTransaction_normal_income() + { + // open new transaction page + driver.findElement(By.xpath("//div[contains(@class, 'new-transaction-button')]//a[contains(text(),'Transaction')]")).click(); + + String name = "My normal transaction"; + String amount = "15.00"; + String description = "Lorem Ipsum dolor sit amet"; + + // fill form + driver.findElement(By.className("buttonIncome")).click(); + driver.findElement(By.id("transaction-name")).sendKeys(name); + driver.findElement(By.id("transaction-amount")).sendKeys(amount); + driver.findElement(By.id("transaction-description")).sendKeys(description); + + // submit form + driver.findElement(By.xpath("//button[@type='submit']")).click(); + + WebDriverWait wait = new WebDriverWait(driver, 5); + wait.until(ExpectedConditions.presenceOfElementLocated(By.cssSelector(".headline-date"))); + + // assert + assertThat(driver.getCurrentUrl()).endsWith("/transactions"); + + List<WebElement> transactionsRows = driver.findElements(By.cssSelector(".transaction-container .hide-on-med-and-down.transaction-row-top")); + assertThat(transactionsRows).hasSize(2); + + final WebElement row = transactionsRows.get(0); + final List<WebElement> columns = row.findElements(By.className("col")); + assertThat(columns).hasSize(6); + + // check columns + final String dateString = new SimpleDateFormat("dd.MM.").format(new Date()); + assertTransactionColumns(columns, dateString, "N", "rgb(255, 255, 255)", false, name, description, amount); + } + + @Test + public void newTransaction_normal_expenditure() + { + // open new transaction page + driver.findElement(By.xpath("//div[contains(@class, 'new-transaction-button')]//a[contains(text(),'Transaction')]")).click(); + + String name = "My normal transaction"; + String amount = "15.00"; + String description = "Lorem Ipsum dolor sit amet"; + + // fill form + driver.findElement(By.className("buttonExpenditure")).click(); + driver.findElement(By.id("transaction-name")).sendKeys(name); + driver.findElement(By.id("transaction-amount")).sendKeys(amount); + driver.findElement(By.id("transaction-description")).sendKeys(description); + + // submit form + driver.findElement(By.xpath("//button[@type='submit']")).click(); + + WebDriverWait wait = new WebDriverWait(driver, 5); + wait.until(ExpectedConditions.presenceOfElementLocated(By.cssSelector(".headline-date"))); + + // assert + assertThat(driver.getCurrentUrl()).endsWith("/transactions"); + + List<WebElement> transactionsRows = driver.findElements(By.cssSelector(".transaction-container .hide-on-med-and-down.transaction-row-top")); + assertThat(transactionsRows).hasSize(2); + + final WebElement row = transactionsRows.get(0); + final List<WebElement> columns = row.findElements(By.className("col")); + assertThat(columns).hasSize(6); + + // check columns + final String dateString = new SimpleDateFormat("dd.MM.").format(new Date()); + assertTransactionColumns(columns, dateString, "N", "rgb(255, 255, 255)", false, name, description, "-" + amount); + } + + private void assertTransactionColumns(List<WebElement> columns, String shortDate, String categoryLetter, String categoryColor, boolean repeatIconVisible, String name, String description, String amount) + { + // date + assertThat(columns.get(0)).hasFieldOrPropertyWithValue("text", shortDate); + + // category + final WebElement categoryCircle = columns.get(1).findElement(By.className("category-circle")); + assertThat(categoryCircle.getCssValue("background-color")).isEqualTo(categoryColor); + assertThat(categoryCircle.findElement(By.tagName("span"))).hasFieldOrPropertyWithValue("text", categoryLetter); + + // icon + final List<WebElement> icons = columns.get(2).findElements(By.tagName("i")); + assertThat(icons).hasSize(1); + assertThat(icons.get(0).isDisplayed()).isEqualTo(repeatIconVisible); + + // name + assertThat(columns.get(3).findElement(By.className("transaction-text")).getText()) + .isEqualTo(name); + + //description + assertThat(columns.get(3).findElement(By.className("italic")).getText()) + .isEqualTo(description); + + // amount + assertThat(columns.get(4).getText()).contains(amount); + } +} \ No newline at end of file