001/** 002 * Copyright 2004-2015 The Kuali Foundation 003 * 004 * Licensed under the Educational Community License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.opensource.org/licenses/ecl2.php 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.kuali.hr.time.clock.web; 017 018import java.math.BigDecimal; 019import java.util.ArrayList; 020import java.util.Collection; 021import java.util.Collections; 022import java.util.Iterator; 023import java.util.LinkedHashMap; 024import java.util.List; 025import java.util.Map; 026 027import org.joda.time.DateTime; 028import org.joda.time.DateTimeZone; 029import org.joda.time.LocalDate; 030import org.junit.Assert; 031import org.junit.Ignore; 032import org.junit.Test; 033import org.kuali.hr.KPMEWebTestCase; 034import org.kuali.hr.util.HtmlUnitUtil; 035import org.kuali.kpme.core.FunctionalTest; 036import org.kuali.kpme.core.service.HrServiceLocator; 037import org.kuali.kpme.core.util.HrTestConstants; 038import org.kuali.kpme.core.util.TKUtils; 039import org.kuali.kpme.tklm.common.TkConstants; 040import org.kuali.kpme.tklm.time.clocklog.ClockLog; 041import org.kuali.kpme.tklm.time.rules.graceperiod.GracePeriodRule; 042import org.kuali.kpme.tklm.time.service.TkServiceLocator; 043import org.kuali.kpme.tklm.time.timeblock.TimeBlock; 044import org.kuali.kpme.tklm.time.timehourdetail.TimeHourDetail; 045import org.kuali.kpme.tklm.time.timesheet.TimesheetDocument; 046import org.kuali.kpme.tklm.time.workflow.TimesheetDocumentHeader; 047import org.kuali.kpme.tklm.utils.TkTestConstants; 048import org.kuali.rice.krad.service.KRADServiceLocator; 049 050import com.gargoylesoftware.htmlunit.NicelyResynchronizingAjaxController; 051import com.gargoylesoftware.htmlunit.WebClient; 052import com.gargoylesoftware.htmlunit.html.HtmlElement; 053import com.gargoylesoftware.htmlunit.html.HtmlPage; 054 055 056@FunctionalTest 057public class ClockWebTest extends KPMEWebTestCase { 058 059 private String tbId; 060 061 public Long maxDocumentId() { 062 Collection aCol = KRADServiceLocator.getBusinessObjectService().findAll(TimesheetDocumentHeader.class); 063 Long maxId = new Long(-1); 064 Iterator<TimesheetDocumentHeader> itr = aCol.iterator(); 065 while (itr.hasNext()) { 066 TimesheetDocumentHeader tdh = itr.next(); 067 Long temp = new Long(tdh.getDocumentId()); 068 if (temp > maxId) { 069 maxId = temp; 070 } 071 } 072 return maxId; 073 } 074 075 public Long maxTimeBlockId() { 076 Collection aCol = KRADServiceLocator.getBusinessObjectService().findAll(TimeBlock.class); 077 Long maxId = new Long(-1); 078 Iterator<TimeBlock> itr = aCol.iterator(); 079 while (itr.hasNext()) { 080 TimeBlock tb = itr.next(); 081 Long temp = new Long(tb.getTkTimeBlockId()); 082 if (temp > maxId) { 083 maxId = temp; 084 } 085 } 086 return maxId; 087 } 088 089 public void createTB() { 090 TimeBlock timeBlock = new TimeBlock(); 091 timeBlock.setUserPrincipalId("admin"); 092 timeBlock.setJobNumber(2L); 093 timeBlock.setWorkArea(1234L); 094 timeBlock.setTask(1L); 095 timeBlock.setEarnCode("RGN"); 096 timeBlock.setBeginTimestamp(TKUtils.getCurrentTimestamp()); 097 timeBlock.setEndTimestamp(TKUtils.getCurrentTimestamp()); 098 TimeHourDetail timeHourDetail = new TimeHourDetail(); 099 timeHourDetail.setEarnCode("RGN"); 100 timeHourDetail.setHours(new BigDecimal(2.0)); 101 timeBlock.getTimeHourDetails().add(timeHourDetail); 102 timeBlock.setHours(new BigDecimal(2.0)); 103 List<TimeBlock> tbList = new ArrayList<TimeBlock>(); 104 String documentId = this.maxDocumentId().toString(); 105 timeBlock.setDocumentId(documentId); 106 tbList.add(timeBlock); 107 TkServiceLocator.getTimeBlockService().saveTimeBlocks(tbList); 108 109 tbId = timeBlock.getTkTimeBlockId(); 110 TimesheetDocument td = TkServiceLocator.getTimesheetService().getTimesheetDocument(documentId); 111 td.setTimeBlocks(tbList); 112 113 } 114 115 @Ignore 116 public void testDistributeTB() throws Exception { 117 String baseUrl = TkTestConstants.Urls.CLOCK_URL; 118 HtmlPage page = HtmlUnitUtil.gotoPageAndLogin(getWebClient(), baseUrl); 119 Assert.assertNotNull(page); 120 Assert.assertTrue("Clock Page contains Distribute Button", page.asText().contains("Distribute Time Blocks")); 121 this.createTB(); 122 updateWebClient(); 123 HtmlElement element = page.getElementByName("distributeTime"); 124 Assert.assertNotNull(element); 125// HtmlPage testPage1 = element.click(); 126// assertTrue("Distribute Page contains Close button", testPage1.asText().contains("Close")); 127// assertTrue("Distribute Page contains Close button", testPage1.asText().contains("Edit")); 128 129 // timeDistribute.jsp 130 String distributeUrl = baseUrl + "?methodToCall=distributeTimeBlocks"; 131 HtmlPage page1 = HtmlUnitUtil.gotoPageAndLogin(getWebClient(), distributeUrl); 132 Assert.assertTrue("Distribute Page contains Close button", page1.asText().contains("Close")); 133 Assert.assertTrue("Distribute Page contains Close button", page1.asText().contains("Edit")); 134 135 element = page1.getElementByName("editTimeBlock"); 136 Assert.assertNotNull(element); 137 Assert.assertTrue("Onclick attribute of Edit button contains", element.getAttribute("onclick").contains("Clock.do?methodToCall=editTimeBlock&editTimeBlockId=")); 138 139 if (tbId == null) { 140 tbId = this.maxTimeBlockId().toString(); 141 } 142 143 //editTimeBlock.jsp 144 String editUrl = baseUrl + "?methodToCall=editTimeBlock&editTimeBlockId=" + tbId; 145 HtmlPage page3 = HtmlUnitUtil.gotoPageAndLogin(getWebClient(), editUrl); 146 147 // editTimeBlock.jsp 148 Assert.assertTrue("Edit Time Blocks Page contains Cancel button", page3.asText().contains("Add")); 149 Assert.assertTrue("Edit Time Blocks Page contains Save button", page3.asText().contains("Save")); 150 Assert.assertTrue("Edit Time Blocks Page contains Cancel button", page3.asText().contains("Cancel")); 151 152 element = page3.getElementByName("addTimeBlock"); 153 Assert.assertNotNull(element); 154 Assert.assertTrue("Onclick attribute of Add button contains", element.getAttribute("onclick").contains("javascript: addTimeBlockRow(this.form);")); 155 156 updateWebClient(); 157 158// HtmlPage page4 = element.click(); 159// assertTrue("Edit Time Blocks Page contains Cancel button", page4.asText().contains("Add")); 160 161 } 162 163 public void updateWebClient() { 164 WebClient webClient = getWebClient(); 165 webClient.setJavaScriptEnabled(true); 166 webClient.setThrowExceptionOnScriptError(false); 167 webClient.setAjaxController(new NicelyResynchronizingAjaxController()); 168 webClient.waitForBackgroundJavaScript(10000); 169 } 170 171 /** 172 * This test is to : 173 * 1) make sure the seconds on clockTimestamp and timestamp are preserved when there is no grace period rule. 174 * 2) the clock in / out button is correctly rendered 175 * 176 * @throws Exception 177 */ 178 @Test 179 public void testClockActionWithoutGracePeriodRule() throws Exception { 180 // Make sure there is no active grace period rule 181 GracePeriodRule gpr = TkServiceLocator.getGracePeriodService().getGracePeriodRule(LocalDate.now()); 182 if (gpr != null && gpr.isActive()) { 183 gpr.setActive(false); 184 KRADServiceLocator.getBusinessObjectService().save(gpr); 185 } 186 187 // Clock in 188 clockIn(); 189 // Make sure clock out button is rendered 190 ClockLog lastClockLog = TkServiceLocator.getClockLogService().getLastClockLog("admin"); 191 // Make sure both timestamps preserve seconds 192 Assert.assertTrue("The seconds on clock timestamp should be preserved", lastClockLog.getClockDateTime().getSecondOfMinute() != 0); 193 Assert.assertTrue("The seconds on timestamp should be preserved", new DateTime(lastClockLog.getTimestamp().getTime()).getSecondOfMinute() != 0); 194 195 // Clock out 196 clockOut(); 197 // Make sure both timestamps preserve seconds 198 lastClockLog = TkServiceLocator.getClockLogService().getLastClockLog("admin"); 199 Assert.assertTrue("The seconds on clock timestamp should be preserved", lastClockLog.getClockDateTime().getSecondOfMinute() != 0); 200 Assert.assertTrue("The seconds on timestamp should be preserved", new DateTime(lastClockLog.getTimestamp().getTime()).getSecondOfMinute() != 0); 201 } 202 203 @Test 204 public void testClockActionWithGracePeriodRule() throws Exception { 205 //clean clock logs 206 KRADServiceLocator.getBusinessObjectService().deleteMatching(ClockLog.class, Collections.singletonMap("principalId", "admin")); 207 GracePeriodRule gpr = new GracePeriodRule(); 208 //gpr.setTkGracePeriodRuleId("1"); 209 gpr.setEffectiveLocalDate(new LocalDate(2010, 1, 1)); 210 gpr.setHourFactor(new BigDecimal(3)); 211 gpr.setTimestamp(TKUtils.getCurrentTimestamp()); 212 213 gpr.setActive(true); 214 KRADServiceLocator.getBusinessObjectService().save(gpr); 215 216 // Clock in 217 clockIn(); 218 // Make sure clock out button is rendered 219 ClockLog lastClockLog = TkServiceLocator.getClockLogService().getLastClockLog("admin"); 220 // Make sure both timestamps preserve seconds 221 Assert.assertTrue("The seconds on clock timestamp should NOT be preserved", lastClockLog.getClockDateTime().getSecondOfMinute() == 0); 222 Assert.assertTrue("The seconds on timestamp should be preserved", new DateTime(lastClockLog.getTimestamp().getTime()).getSecondOfMinute() != 0); 223 224 // Clock out 225 clockOut(); 226 // Make sure both timestamps preserve seconds 227 lastClockLog = TkServiceLocator.getClockLogService().getLastClockLog("admin"); 228 Assert.assertTrue("The seconds on clock timestamp should NOT be preserved", lastClockLog.getClockDateTime().getSecondOfMinute() == 0); 229 Assert.assertTrue("The seconds on timestamp should be preserved", new DateTime(lastClockLog.getTimestamp().getTime()).getSecondOfMinute() != 0); 230 231 232 } 233 234 235 @Test 236 public void testClockInOutWithTimezone() throws Exception { 237 HtmlPage page = HtmlUnitUtil.gotoPageAndLogin(getWebClient(), TkTestConstants.Urls.CLOCK_URL,true); 238 Assert.assertNotNull(page); 239 240 Map<String, Object> criteria = new LinkedHashMap<String, Object>(); 241 criteria.put("selectedAssignment", new String[]{HrTestConstants.FormElementTypes.DROPDOWN, "30_30_30"}); 242 // choose the first assignment from the drop down 243 page = HtmlUnitUtil.fillOutForm(page, criteria); 244 Assert.assertNotNull(page); 245 // clock in 246 page = HtmlUnitUtil.clickButton(page, "clockAction"); 247 // clock out 248 page = HtmlUnitUtil.clickButton(page, "clockAction"); 249 HtmlUnitUtil.createTempFile(page); 250 251 DateTimeZone dateTimeZone = DateTimeZone.forID(HrServiceLocator.getTimezoneService().getUserTimezone()); 252 DateTime dateTime = new DateTime().withZone(dateTimeZone); 253 String timeZone = dateTime.toString("zzzz"); 254 Assert.assertTrue("Time zone information is incorrect", page.asText().contains(timeZone)); 255 } 256 257 private HtmlPage clockIn() throws Exception { 258 259 // Clock in 260 HtmlPage page = clockAction(TkConstants.CLOCK_IN); 261 262 // Make sure clock in button is rendered 263 HtmlUnitUtil.createTempFile(page); 264 Assert.assertTrue("The clock out button should have displayed", page.asText().contains("Clock Out")); 265 Assert.assertTrue("The clock out button should have displayed", page.asText().contains("Take Lunch")); 266 267 return page; 268 } 269 270 private HtmlPage clockOut() throws Exception { 271 DateTime dateTime = new DateTime(); 272 if (dateTime.getSecondOfMinute() >= 58 273 || dateTime.getSecondOfMinute() == 0) { 274 Thread.sleep(4000); 275 } 276 // Clock out 277 HtmlPage page = clockAction(TkConstants.CLOCK_OUT); 278 279 // Make sure clock in button is rendered 280 HtmlUnitUtil.createTempFile(page); 281 Assert.assertTrue("The clock out button should have displayed", page.asText().contains("Clock In")); 282 283 return page; 284 } 285 286 /** 287 * This method is used for clocking in and out. 288 * For some reason, htmlunit couldn't click the clock action button correctly. 289 * It's probably because we bind a onClick event to the button instead of submitting the form. 290 * 291 * @param clockAction 292 * @return HtmlPage page 293 */ 294 private HtmlPage clockAction(String clockAction) throws Exception { 295 DateTime dateTime = new DateTime(); 296 if (dateTime.getSecondOfMinute() >= 58 297 || dateTime.getSecondOfMinute() == 0) { 298 Thread.sleep(4000); 299 } 300 String baseUrl = TkTestConstants.Urls.CLOCK_URL; 301 String actionUrl = baseUrl + "?methodToCall=clockAction&selectedAssignment=30_30_30¤tClockAction=" + clockAction; 302 HtmlPage page = HtmlUnitUtil.gotoPageAndLogin(getWebClient(), actionUrl); 303 Assert.assertNotNull("The login page shouldn't be null", page); 304 Thread.sleep(3000); 305 return page; 306 } 307 308 309}