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.kns.service.KNSServiceLocator; 052import org.kuali.rice.krad.service.KRADServiceLocator; 053 054import com.gargoylesoftware.htmlunit.NicelyResynchronizingAjaxController; 055import com.gargoylesoftware.htmlunit.WebClient; 056import com.gargoylesoftware.htmlunit.html.HtmlElement; 057import com.gargoylesoftware.htmlunit.html.HtmlPage; 058import org.kuali.rice.krad.service.KRADServiceLocatorWeb; 059 060 061@FunctionalTest 062public class ClockWebTest extends KPMEWebTestCase { 063 064 private String tbId; 065 066 public Long maxDocumentId() { 067 Collection aCol = KRADServiceLocatorWeb.getLegacyDataAdapter().findAll(TimesheetDocumentHeader.class); 068 Long maxId = new Long(-1); 069 Iterator<TimesheetDocumentHeader> itr = aCol.iterator(); 070 while (itr.hasNext()) { 071 TimesheetDocumentHeader tdh = itr.next(); 072 Long temp = new Long(tdh.getDocumentId()); 073 if (temp > maxId) { 074 maxId = temp; 075 } 076 } 077 return maxId; 078 } 079 080 public Long maxTimeBlockId() { 081 Collection aCol = KRADServiceLocatorWeb.getLegacyDataAdapter().findAll(TimeBlockBo.class); 082 Long maxId = new Long(-1); 083 Iterator<TimeBlockBo> itr = aCol.iterator(); 084 while (itr.hasNext()) { 085 TimeBlockBo tb = itr.next(); 086 Long temp = new Long(tb.getTkTimeBlockId()); 087 if (temp > maxId) { 088 maxId = temp; 089 } 090 } 091 return maxId; 092 } 093 094 public void createTB() { 095 TimeBlockBo timeBlock = new TimeBlockBo(); 096 timeBlock.setUserPrincipalId("admin"); 097 timeBlock.setJobNumber(2L); 098 timeBlock.setWorkArea(1234L); 099 timeBlock.setTask(1L); 100 timeBlock.setEarnCode("RGN"); 101 timeBlock.setBeginTimestamp(TKUtils.getCurrentTimestamp()); 102 timeBlock.setEndTimestamp(TKUtils.getCurrentTimestamp()); 103 TimeHourDetailBo timeHourDetail = new TimeHourDetailBo(); 104 timeHourDetail.setEarnCode("RGN"); 105 timeHourDetail.setHours(new BigDecimal(2.0)); 106 timeBlock.getTimeHourDetails().add(timeHourDetail); 107 timeBlock.setHours(new BigDecimal(2.0)); 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 KRADServiceLocatorWeb.getLegacyDataAdapter().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 KNSServiceLocator.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 KNSServiceLocator.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}