/* * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package net.rrm.ehour.ui.timesheet.panel; import com.google.common.collect.Lists; import net.rrm.ehour.data.DateRange; import net.rrm.ehour.domain.*; import net.rrm.ehour.project.status.ProjectAssignmentStatus; import net.rrm.ehour.timesheet.dto.WeekOverview; import net.rrm.ehour.timesheet.service.IOverviewTimesheet; import net.rrm.ehour.timesheet.service.IPersistTimesheet; import net.rrm.ehour.timesheet.service.TimesheetLockService; import net.rrm.ehour.ui.common.BaseSpringWebAppTester; import net.rrm.ehour.ui.common.session.EhourWebSession; import net.rrm.ehour.ui.timesheet.dto.Timesheet; import net.rrm.ehour.ui.timesheet.model.TimesheetContainer; import net.rrm.ehour.ui.timesheet.model.TimesheetModel; import net.rrm.ehour.ui.timesheet.panel.dailycomments.DailyCommentPanelFactory; import net.rrm.ehour.ui.timesheet.panel.renderer.SectionRenderFactory; import net.rrm.ehour.ui.timesheet.panel.renderer.SectionRenderFactoryCollection; import net.rrm.ehour.ui.timesheet.panel.renderer.TimesheetIconRenderFactoryCollection; import net.rrm.ehour.ui.timesheet.panel.weeklycomment.WeeklyCommentPanelRenderFactory; import net.rrm.ehour.user.service.UserService; import org.apache.wicket.Component; import org.apache.wicket.extensions.ajax.markup.html.modal.ModalWindow; import org.apache.wicket.markup.html.basic.Label; import org.apache.wicket.util.tester.FormTester; import org.joda.time.DateTime; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.runners.MockitoJUnitRunner; import java.util.*; import static org.junit.Assert.*; import static org.mockito.Matchers.any; import static org.mockito.Mockito.when; @RunWith(MockitoJUnitRunner.class) public class TimesheetPanelTest extends BaseSpringWebAppTester { private static final String TIMESHEET_PATH = "panel:timesheetFrame:timesheetFrame_body:timesheetForm"; private static final String DAY1_PATH = "blueFrame:blueFrame_body:customers:0:rows:0:days:0"; private static final String DAY1_FULL_PATH = TIMESHEET_PATH + ":" + DAY1_PATH; private static final User USER = new User(1); private static final String DAY_INPUT = ":day:day"; @Mock private IPersistTimesheet persistTimesheet; @Mock private IOverviewTimesheet overviewTimesheet; @Mock private UserService userService; @Mock private TimesheetLockService lockService; private DateTime jodaNow = DateTime.now().withTimeAtStartOfDay(); private Date dateNow = jodaNow.toDate(); private GregorianCalendar calNow = jodaNow.toGregorianCalendar(); private Date tomorrow = jodaNow.plusDays(1).toDate(); private final ArrayList<Date> tomorrowAsList = Lists.newArrayList(tomorrow); @Before public void setup() { getConfig().setCompleteDayHours(8l); EhourWebSession.getSession().reloadConfig(); getMockContext().putBean(persistTimesheet); getMockContext().putBean(overviewTimesheet); getMockContext().putBean("userService", userService); getMockContext().putBean(lockService); getMockContext().putBean(new TimesheetModel()); TimesheetIconRenderFactoryCollection iconRenderFactoryCollection = new TimesheetIconRenderFactoryCollection(Lists.newArrayList(new DailyCommentPanelFactory())); getMockContext().putBean(iconRenderFactoryCollection); SectionRenderFactoryCollection sectionRenderFactoryCollection = new SectionRenderFactoryCollection(Lists.<SectionRenderFactory>newArrayList(new WeeklyCommentPanelRenderFactory())); getMockContext().putBean(sectionRenderFactoryCollection); } @Test public void addDayComment() { startAndReplayWithDefaultWeekOverview(); final String comment = "commentaar"; ModalWindow window = openCommentWindow(DAY1_FULL_PATH); assertTrue(window.isShown()); FormTester timesheetFormTester = tester.newFormTester(TIMESHEET_PATH); timesheetFormTester.setValue(DAY1_PATH + ":options:1:dayWin:content:comment", comment); tester.executeAjaxEvent(DAY1_FULL_PATH + ":options:1:dayWin:content:comment", "onchange"); tester.executeAjaxEvent(DAY1_FULL_PATH + ":options:1:dayWin:content:submit", "onclick"); Timesheet timesheet = ((TimesheetContainer) tester.getComponentFromLastRenderedPage("panel").getDefaultModelObject()).getTimesheet(); assertEquals(comment, timesheet.getTimesheetEntries().get(0).getComment()); tester.assertNoErrorMessage(); } private ModalWindow openCommentWindow(String path) { ModalWindow window = (ModalWindow) tester.getComponentFromLastRenderedPage(path + ":options:1:dayWin"); tester.executeAjaxEvent(path + ":options:1:commentLink", "onclick"); return window; } @Test public void addDayCommentCancelled() { startAndReplayWithDefaultWeekOverview(); final String comment = "commentaar"; clickDay1(); FormTester formTester = tester.newFormTester(TIMESHEET_PATH); formTester.setValue(DAY1_PATH + ":options:1:dayWin:content:comment", comment); tester.executeAjaxEvent(DAY1_FULL_PATH + ":options:1:dayWin:content:comment", "onchange"); tester.executeAjaxEvent(DAY1_FULL_PATH + ":options:1:dayWin:content:cancel", "onclick"); Timesheet timesheet = ((TimesheetContainer) tester.getComponentFromLastRenderedPage("panel").getDefaultModelObject()).getTimesheet(); assertNull(timesheet.getTimesheetEntries().get(0).getComment()); tester.assertNoErrorMessage(); } private void clickDay1() { tester.executeAjaxEvent(DAY1_FULL_PATH + ":options:1:commentLink", "onclick"); } @Test public void shouldBookAllHours() { startAndReplayWithDefaultWeekOverview(); tester.executeAjaxEvent(TIMESHEET_PATH + ":blueFrame:blueFrame_body:customers:0:rows:0:bookWholeWeek", "onclick"); tester.assertNoErrorMessage(); Label grandTotalLabel = (Label) tester.getComponentFromLastRenderedPage(TIMESHEET_PATH + ":blueFrame:blueFrame_body:grandTotal"); assertEquals(40f, (Float) grandTotalLabel.getDefaultModelObject(), 0.01f); tester.assertNoErrorMessage(); } @Test public void updateCounts() { startAndReplayWithDefaultWeekOverview(); FormTester formTester = tester.newFormTester(TIMESHEET_PATH); formTester.setValue(DAY1_PATH + DAY_INPUT, "12"); tester.executeAjaxEvent(DAY1_FULL_PATH + DAY_INPUT, "onblur"); tester.assertNoErrorMessage(); Label grandTotalLabel = (Label) tester.getComponentFromLastRenderedPage(TIMESHEET_PATH + ":blueFrame:blueFrame_body:grandTotal"); assertEquals(12f, (Float) grandTotalLabel.getDefaultModelObject(), 0.01f); } @Test public void moveToNextWeek() { startAndReplayWithDefaultWeekOverview(); tester.executeAjaxEvent("panel:timesheetFrame:title:nextWeek", "onclick"); Calendar cal = getWebApp().getSession().getNavCalendar(); Calendar now = GregorianCalendar.getInstance(); now.add(Calendar.DAY_OF_YEAR, 1); assertTrue(now.getTime().before(cal.getTime())); } @Test public void shouldNotResendUnmodifiedEntries() { startAndReplayWithDefaultWeekOverview(); FormTester formTester = tester.newFormTester(TIMESHEET_PATH); formTester.setValue(DAY1_PATH + DAY_INPUT, "12"); tester.executeAjaxEvent(DAY1_FULL_PATH + DAY_INPUT, "onblur"); tester.assertNoErrorMessage(); tester.assertContains(DAY1_PATH + DAY_INPUT); //changing another field should not resend the unmodified day1 formTester.setValue("blueFrame:blueFrame_body:customers:0:rows:0:days:1:day:day", "8"); tester.executeAjaxEvent(TIMESHEET_PATH + ":blueFrame:blueFrame_body:customers:0:rows:0:days:1:day:day", "onblur"); tester.assertNoErrorMessage(); tester.assertContains("blueFrame:blueFrame_body:customers:0:rows:0:days:1:day:day"); } @Test public void shouldResetErrorState() { startAndReplayWithDefaultWeekOverview(); FormTester formTester = tester.newFormTester(TIMESHEET_PATH); formTester.setValue(DAY1_PATH + DAY_INPUT, "12"); tester.executeAjaxEvent(DAY1_FULL_PATH + DAY_INPUT, "onblur"); tester.assertNoErrorMessage(); tester.assertContains(DAY1_PATH + DAY_INPUT); tester.assertContainsNot("color: #ff0000"); formTester.setValue(DAY1_PATH + DAY_INPUT, "ff"); tester.executeAjaxEvent(DAY1_FULL_PATH + DAY_INPUT, "onblur"); tester.assertContains(DAY1_PATH + DAY_INPUT); tester.assertContains("color: #ff0000"); tester.assertErrorMessages("day.IConverter.Float"); formTester.setValue(DAY1_PATH + DAY_INPUT, "1"); tester.executeAjaxEvent(DAY1_FULL_PATH + DAY_INPUT, "onblur"); tester.assertContains(DAY1_PATH + DAY_INPUT); tester.assertContainsNot("color: #ff0000"); } @SuppressWarnings("unchecked") @Test public void shouldSubmitSuccessful() { when(persistTimesheet.persistTimesheetWeek(any(Collection.class), any(TimesheetComment.class), any(DateRange.class), any(User.class))) .thenReturn(new ArrayList<ProjectAssignmentStatus>()); startAndReplayWithDefaultWeekOverview(); tester.executeAjaxEvent(TIMESHEET_PATH + ":submitButton", "onclick"); tester.assertNoErrorMessage(); } @Test public void shouldDisableInputForLockedDays() { startWithLockedDays(Arrays.asList(tomorrow)); tester.assertComponent(TIMESHEET_PATH + ":blueFrame:blueFrame_body:customers:0:rows:0:days:1:day:day", Label.class); tester.assertComponent(TIMESHEET_PATH + ":blueFrame:blueFrame_body:customers:0:rows:0:days:2:day:day", TimesheetTextField.class); tester.assertNoErrorMessage(); } @Test public void shouldDisableDailyCommentInputForLockedDaysWithExistingComment() { // given List<Date> lockedDays = tomorrowAsList; startWithLockedDays(lockedDays); DateRange nextWeekRange = new DateRange(dateNow, jodaNow.plusWeeks(1).toDate()); TimesheetEntry timesheetEntry = TimesheetEntryObjectMother.createTimesheetEntry(1, tomorrow, 5); timesheetEntry.setComment("Comment"); List<TimesheetEntry> entries = Arrays.asList(timesheetEntry); List<ProjectAssignment> assignments = Arrays.asList(ProjectAssignmentObjectMother.createProjectAssignment(1)); WeekOverview overview = new WeekOverview(entries, null, assignments, nextWeekRange, USER, lockedDays); whenDefaultWeekOverview(overview); // when tester.startComponentInPage(new TimesheetPanel("panel", USER, new GregorianCalendar())); // then openCommentWindow(TIMESHEET_PATH + ":blueFrame:blueFrame_body:customers:0:rows:0:days:1"); tester.assertComponent(TIMESHEET_PATH + ":blueFrame:blueFrame_body:customers:0:rows:0:days:1:options:1:dayWin:content:comment", Label.class); tester.assertNoErrorMessage(); tester.assertNoInfoMessage(); } @Test public void shouldHideCommentInputLinkForLockedDaysWithoutComment() { startWithLockedDays(tomorrowAsList); Component commentLink = tester.getComponentFromLastRenderedPage(TIMESHEET_PATH + ":blueFrame:blueFrame_body:customers:0:rows:0:days:1:options:1:commentLink"); assertNull(commentLink); // null = not visible... tester.assertNoErrorMessage(); } @Test public void shouldAddLockedIconInDayForLockedDay() { startWithLockedDays(tomorrowAsList); String path = TIMESHEET_PATH + ":blueFrame:blueFrame_body:day2Label:lock:lockedContainer"; tester.assertVisible(path); tester.assertNoErrorMessage(); } @Test public void shouldHideBookWholeWeekIconWhenDisabledInConfig() { webApp.setEnableBookWholeWeek(false); startAndReplayWithDefaultWeekOverview(); Component book = tester.getComponentFromLastRenderedPage(TIMESHEET_PATH + ":blueFrame:blueFrame_body:customers:0:rows:0:bookWholeWeek"); // not visible = null assertNull(book); } @Test public void shouldHideBookWholeWeekIconWhenADayIsLocked() { startWithLockedDays(tomorrowAsList); Component book = tester.getComponentFromLastRenderedPage(TIMESHEET_PATH + ":blueFrame:blueFrame_body:customers:0:rows:0:bookWholeWeek"); // not visible = null assertNull(book); } @Test public void shouldHideStoreButtonsWhenWholeWeekIsLocked() { List<Date> dates = Lists.newArrayList(); for (int i = 0; i < 8; i++) { dates.add(jodaNow.plusDays(i).toDate()); } startWithLockedDays(dates); assertNull(tester.getComponentFromLastRenderedPage(TIMESHEET_PATH + ":submitButton")); assertNull(tester.getComponentFromLastRenderedPage(TIMESHEET_PATH + ":blueFrame:blueFrame_body:submitButtonTop")); } private void startAndReplayWithDefaultWeekOverview() { startWithLockedDays(Lists.<Date>newArrayList()); } private void startWithLockedDays(List<Date> lockedDays) { whenDefaultWeekOverview(withDefaultWeekOverview(lockedDays)); tester.startComponentInPage(new TimesheetPanel("panel", USER, calNow)); } private void whenDefaultWeekOverview(WeekOverview overview) { when(overviewTimesheet.getWeekOverview(any(User.class), any(Calendar.class))).thenReturn(overview); } private WeekOverview withDefaultWeekOverview(List<Date> lockedDates) { DateRange nextWeekRange = new DateRange(dateNow, jodaNow.plusWeeks(1).toDate()); List<TimesheetEntry> entries = Arrays.asList(TimesheetEntryObjectMother.createTimesheetEntry(1, dateNow, 5)); List<ProjectAssignment> assignments = Arrays.asList(ProjectAssignmentObjectMother.createProjectAssignment(1)); return new WeekOverview(entries, null, assignments, nextWeekRange, USER, lockedDates); } }