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