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 timeBlock.getTimeHourDetails().add(timeHourDetail); 105 timeBlock.setHours(new BigDecimal(2.0)); 106 List<TimeBlock> tbList = new ArrayList<TimeBlock>(); 107 String documentId = this.maxDocumentId().toString(); 108 timeBlock.setDocumentId(documentId); 109 tbList.add(TimeBlockBo.to(timeBlock)); 110 TkServiceLocator.getTimeBlockService().saveTimeBlocks(tbList); 111 112 tbId = timeBlock.getTkTimeBlockId(); 113 TimesheetDocument td = TkServiceLocator.getTimesheetService().getTimesheetDocument(documentId); 114 td.setTimeBlocks(tbList); 115 116 } 117 118 @Ignore 119 public void testDistributeTB() throws Exception { 120 String baseUrl = TkTestConstants.Urls.CLOCK_URL; 121 HtmlPage page = HtmlUnitUtil.gotoPageAndLogin(getWebClient(), baseUrl); 122 Assert.assertNotNull(page); 123 Assert.assertTrue("Clock Page contains Distribute Button", page.asText().contains("Distribute Time Blocks")); 124 this.createTB(); 125 updateWebClient(); 126 HtmlElement element = page.getElementByName("distributeTime"); 127 Assert.assertNotNull(element); 128// HtmlPage testPage1 = element.click(); 129// assertTrue("Distribute Page contains Close button", testPage1.asText().contains("Close")); 130// assertTrue("Distribute Page contains Close button", testPage1.asText().contains("Edit")); 131 132 // timeDistribute.jsp 133 String distributeUrl = baseUrl + "?methodToCall=distributeTimeBlocks"; 134 HtmlPage page1 = HtmlUnitUtil.gotoPageAndLogin(getWebClient(), distributeUrl); 135 Assert.assertTrue("Distribute Page contains Close button", page1.asText().contains("Close")); 136 Assert.assertTrue("Distribute Page contains Close button", page1.asText().contains("Edit")); 137 138 element = page1.getElementByName("editTimeBlock"); 139 Assert.assertNotNull(element); 140 Assert.assertTrue("Onclick attribute of Edit button contains", element.getAttribute("onclick").contains("Clock.do?methodToCall=editTimeBlock&editTimeBlockId=")); 141 142 if (tbId == null) { 143 tbId = this.maxTimeBlockId().toString(); 144 } 145 146 //editTimeBlock.jsp 147 String editUrl = baseUrl + "?methodToCall=editTimeBlock&editTimeBlockId=" + tbId; 148 HtmlPage page3 = HtmlUnitUtil.gotoPageAndLogin(getWebClient(), editUrl); 149 150 // editTimeBlock.jsp 151 Assert.assertTrue("Edit Time Blocks Page contains Cancel button", page3.asText().contains("Add")); 152 Assert.assertTrue("Edit Time Blocks Page contains Save button", page3.asText().contains("Save")); 153 Assert.assertTrue("Edit Time Blocks Page contains Cancel button", page3.asText().contains("Cancel")); 154 155 element = page3.getElementByName("addTimeBlock"); 156 Assert.assertNotNull(element); 157 Assert.assertTrue("Onclick attribute of Add button contains", element.getAttribute("onclick").contains("javascript: addTimeBlockRow(this.form);")); 158 159 updateWebClient(); 160 161// HtmlPage page4 = element.click(); 162// assertTrue("Edit Time Blocks Page contains Cancel button", page4.asText().contains("Add")); 163 164 } 165 166 public void updateWebClient() { 167 WebClient webClient = getWebClient(); 168 webClient.getOptions().setJavaScriptEnabled(true); 169 webClient.getOptions().setThrowExceptionOnScriptError(false); 170 webClient.setAjaxController(new NicelyResynchronizingAjaxController()); 171 webClient.waitForBackgroundJavaScript(10000); 172 } 173 174 /** 175 * This test is to : 176 * 1) make sure the seconds on clockTimestamp and timestamp are preserved when there is no grace period rule. 177 * 2) the clock in / out button is correctly rendered 178 * 179 * @throws Exception 180 */ 181 @Test 182 public void testClockActionWithoutGracePeriodRule() throws Exception { 183 // Make sure there is no active grace period rule 184 GracePeriodRule gpr = TkServiceLocator.getGracePeriodService().getGracePeriodRule(LocalDate.now()); 185 if (gpr != null && gpr.isActive()) { 186 gpr.setActive(false); 187 KRADServiceLocator.getBusinessObjectService().save(gpr); 188 } 189 190 // Clock in 191 clockIn(); 192 // Make sure clock out button is rendered 193 ClockLog lastClockLog = TkServiceLocator.getClockLogService().getLastClockLog("admin"); 194 // Make sure both timestamps preserve seconds 195 Assert.assertTrue("The seconds on clock timestamp should be preserved", lastClockLog.getClockDateTime().getSecondOfMinute() != 0); 196 Assert.assertTrue("The seconds on timestamp should be preserved", lastClockLog.getCreateTime().getSecondOfMinute() != 0); 197 198 // Clock out 199 clockOut(); 200 // Make sure both timestamps preserve seconds 201 lastClockLog = TkServiceLocator.getClockLogService().getLastClockLog("admin"); 202 Assert.assertTrue("The seconds on clock timestamp should be preserved", lastClockLog.getClockDateTime().getSecondOfMinute() != 0); 203 Assert.assertTrue("The seconds on timestamp should be preserved", lastClockLog.getCreateTime().getSecondOfMinute() != 0); 204 } 205 206 @Test 207 public void testClockActionWithGracePeriodRule() throws Exception { 208 // clean clock logs 209 KRADServiceLocator.getBusinessObjectService().deleteMatching( 210 ClockLogBo.class, 211 Collections.singletonMap("principalId", "admin")); 212 GracePeriodRule gpr = new GracePeriodRule(); 213 // gpr.setTkGracePeriodRuleId("1"); 214 gpr.setEffectiveLocalDate(new LocalDate(2010, 1, 1)); 215 gpr.setHourFactor(new BigDecimal(3)); 216 gpr.setTimestamp(TKUtils.getCurrentTimestamp()); 217 gpr.setUserPrincipalId("admin"); 218 219 gpr.setActive(true); 220 KRADServiceLocator.getBusinessObjectService().save(gpr); 221 222 // Clock in 223 clockIn(); 224 // Make sure clock out button is rendered 225 ClockLog lastClockLog = TkServiceLocator.getClockLogService() 226 .getLastClockLog("admin"); 227 // Make sure both timestamps preserve seconds 228 Assert.assertTrue( 229 "The seconds on clock timestamp should NOT be preserved", 230 lastClockLog.getClockDateTime().getSecondOfMinute() == 0); 231 Assert.assertTrue("The seconds on timestamp should be preserved", 232 lastClockLog.getCreateTime().getSecondOfMinute() != 0); 233 234 // Clock out 235 clockOut(); 236 // Make sure both timestamps preserve seconds 237 lastClockLog = TkServiceLocator.getClockLogService().getLastClockLog( 238 "admin"); 239 Assert.assertTrue( 240 "The seconds on clock timestamp should NOT be preserved", 241 lastClockLog.getClockDateTime().getSecondOfMinute() == 0); 242 Assert.assertTrue("The seconds on timestamp should be preserved", 243 lastClockLog.getCreateTime().getSecondOfMinute() != 0); 244 } 245 246 247 @Test 248 public void testClockInOutWithTimezone() throws Exception { 249 HtmlPage page = HtmlUnitUtil.gotoPageAndLogin(getWebClient(), TkTestConstants.Urls.CLOCK_URL,true); 250 Assert.assertNotNull(page); 251 252 Map<String, Object> criteria = new LinkedHashMap<String, Object>(); 253 criteria.put("selectedAssignment", new String[]{HrTestConstants.FormElementTypes.DROPDOWN, "IU-IN_30_30_30"}); 254 // choose the first assignment from the drop down 255 page = HtmlUnitUtil.fillOutForm(page, criteria); 256 Assert.assertNotNull(page); 257 // clock in 258 page = HtmlUnitUtil.clickButton(page, "clockAction"); 259 // clock out 260 page = HtmlUnitUtil.clickButton(page, "clockAction"); 261 HtmlUnitUtil.createTempFile(page); 262 263 DateTimeZone dateTimeZone = DateTimeZone.forID(HrServiceLocator.getTimezoneService().getUserTimezone()); 264 DateTime dateTime = new DateTime().withZone(dateTimeZone); 265 String timeZone = dateTime.toString("zzzz"); 266 Assert.assertTrue("Time zone information is incorrect", page.asText().contains(timeZone)); 267 } 268 269 private HtmlPage clockIn() throws Exception { 270 271 // Clock in 272 HtmlPage page = clockAction(TkConstants.CLOCK_IN); 273 274 // Make sure clock in button is rendered 275 HtmlUnitUtil.createTempFile(page); 276 Assert.assertTrue("The clock out button should have displayed", page.asText().contains("Clock Out")); 277 // KPME-3532 system lunch is always off now, so lunch button should never show up 278 // Assert.assertTrue("The clock out button should have displayed", page.asText().contains("Take Lunch")); 279 280 return page; 281 } 282 283 private HtmlPage clockOut() throws Exception { 284 DateTime dateTime = new DateTime(); 285 if (dateTime.getSecondOfMinute() >= 58 286 || dateTime.getSecondOfMinute() == 0) { 287 Thread.sleep(4000); 288 } 289 // Clock out 290 HtmlPage page = clockAction(TkConstants.CLOCK_OUT); 291 292 // Make sure clock in button is rendered 293 HtmlUnitUtil.createTempFile(page); 294 Assert.assertTrue("The clock out button should have displayed", page.asText().contains("Clock In")); 295 296 return page; 297 } 298 299 /** 300 * This method is used for clocking in and out. 301 * For some reason, htmlunit couldn't click the clock action button correctly. 302 * It's probably because we bind a onClick event to the button instead of submitting the form. 303 * 304 * @param clockAction 305 * @return HtmlPage page 306 */ 307 private HtmlPage clockAction(String clockAction) throws Exception { 308 DateTime dateTime = new DateTime(); 309 if (dateTime.getSecondOfMinute() >= 58 310 || dateTime.getSecondOfMinute() == 0) { 311 Thread.sleep(4000); 312 } 313 String baseUrl = TkTestConstants.Urls.CLOCK_URL; 314 String actionUrl = baseUrl + "?methodToCall=clockAction&selectedAssignment=IU-IN_30_30_30¤tClockAction=" + clockAction; 315 HtmlPage page = HtmlUnitUtil.gotoPageAndLogin(getWebClient(), actionUrl); 316 Assert.assertNotNull("The login page shouldn't be null", page); 317 Thread.sleep(3000); 318 return page; 319 } 320 321 322}