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 String gpRuleConfig = ConfigContext.getCurrentContextConfig().getProperty(TkConstants.KPME_GRACE_PERIOD_RULE_CONFIG); 209 if(gpRuleConfig != null && gpRuleConfig.equals("CLOCK")) { 210 //clean clock logs 211 KRADServiceLocator.getBusinessObjectService().deleteMatching(ClockLogBo.class, 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().getLastClockLog("admin"); 226 // Make sure both timestamps preserve seconds 227 Assert.assertTrue("The seconds on clock timestamp should NOT be preserved", lastClockLog.getClockDateTime().getSecondOfMinute() == 0); 228 Assert.assertTrue("The seconds on timestamp should be preserved", lastClockLog.getCreateTime().getSecondOfMinute() != 0); 229 230 // Clock out 231 clockOut(); 232 // Make sure both timestamps preserve seconds 233 lastClockLog = TkServiceLocator.getClockLogService().getLastClockLog("admin"); 234 Assert.assertTrue("The seconds on clock timestamp should NOT be preserved", lastClockLog.getClockDateTime().getSecondOfMinute() == 0); 235 Assert.assertTrue("The seconds on timestamp should be preserved", lastClockLog.getCreateTime().getSecondOfMinute() != 0); 236 237 } 238 } 239 240 241 @Test 242 public void testClockInOutWithTimezone() throws Exception { 243 HtmlPage page = HtmlUnitUtil.gotoPageAndLogin(getWebClient(), TkTestConstants.Urls.CLOCK_URL,true); 244 Assert.assertNotNull(page); 245 246 Map<String, Object> criteria = new LinkedHashMap<String, Object>(); 247 criteria.put("selectedAssignment", new String[]{HrTestConstants.FormElementTypes.DROPDOWN, "IU-IN_30_30_30"}); 248 // choose the first assignment from the drop down 249 page = HtmlUnitUtil.fillOutForm(page, criteria); 250 Assert.assertNotNull(page); 251 // clock in 252 page = HtmlUnitUtil.clickButton(page, "clockAction"); 253 // clock out 254 page = HtmlUnitUtil.clickButton(page, "clockAction"); 255 HtmlUnitUtil.createTempFile(page); 256 257 DateTimeZone dateTimeZone = DateTimeZone.forID(HrServiceLocator.getTimezoneService().getUserTimezone()); 258 DateTime dateTime = new DateTime().withZone(dateTimeZone); 259 String timeZone = dateTime.toString("zzzz"); 260 Assert.assertTrue("Time zone information is incorrect", page.asText().contains(timeZone)); 261 } 262 263 private HtmlPage clockIn() throws Exception { 264 265 // Clock in 266 HtmlPage page = clockAction(TkConstants.CLOCK_IN); 267 268 // Make sure clock in button is rendered 269 HtmlUnitUtil.createTempFile(page); 270 Assert.assertTrue("The clock out button should have displayed", page.asText().contains("Clock Out")); 271 // KPME-3532 system lunch is always off now, so lunch button should never show up 272 // Assert.assertTrue("The clock out button should have displayed", page.asText().contains("Take Lunch")); 273 274 return page; 275 } 276 277 private HtmlPage clockOut() throws Exception { 278 DateTime dateTime = new DateTime(); 279 if (dateTime.getSecondOfMinute() >= 58 280 || dateTime.getSecondOfMinute() == 0) { 281 Thread.sleep(4000); 282 } 283 // Clock out 284 HtmlPage page = clockAction(TkConstants.CLOCK_OUT); 285 286 // Make sure clock in button is rendered 287 HtmlUnitUtil.createTempFile(page); 288 Assert.assertTrue("The clock out button should have displayed", page.asText().contains("Clock In")); 289 290 return page; 291 } 292 293 /** 294 * This method is used for clocking in and out. 295 * For some reason, htmlunit couldn't click the clock action button correctly. 296 * It's probably because we bind a onClick event to the button instead of submitting the form. 297 * 298 * @param clockAction 299 * @return HtmlPage page 300 */ 301 private HtmlPage clockAction(String clockAction) throws Exception { 302 DateTime dateTime = new DateTime(); 303 if (dateTime.getSecondOfMinute() >= 58 304 || dateTime.getSecondOfMinute() == 0) { 305 Thread.sleep(4000); 306 } 307 String baseUrl = TkTestConstants.Urls.CLOCK_URL; 308 String actionUrl = baseUrl + "?methodToCall=clockAction&selectedAssignment=IU-IN_30_30_30¤tClockAction=" + clockAction; 309 HtmlPage page = HtmlUnitUtil.gotoPageAndLogin(getWebClient(), actionUrl); 310 Assert.assertNotNull("The login page shouldn't be null", page); 311 Thread.sleep(3000); 312 return page; 313 } 314 315 316}