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 */
016 package org.kuali.hr.time.clock.web;
017
018 import java.math.BigDecimal;
019 import java.util.ArrayList;
020 import java.util.Collection;
021 import java.util.Collections;
022 import java.util.Iterator;
023 import java.util.LinkedHashMap;
024 import java.util.List;
025 import java.util.Map;
026
027 import org.joda.time.DateTime;
028 import org.joda.time.DateTimeZone;
029 import org.joda.time.LocalDate;
030 import org.junit.Assert;
031 import org.junit.Ignore;
032 import org.junit.Test;
033 import org.kuali.hr.KPMEWebTestCase;
034 import org.kuali.hr.util.HtmlUnitUtil;
035 import org.kuali.kpme.core.FunctionalTest;
036 import org.kuali.kpme.core.service.HrServiceLocator;
037 import org.kuali.kpme.core.util.HrTestConstants;
038 import org.kuali.kpme.core.util.TKUtils;
039 import org.kuali.kpme.tklm.common.TkConstants;
040 import org.kuali.kpme.tklm.time.clocklog.ClockLog;
041 import org.kuali.kpme.tklm.time.rules.graceperiod.GracePeriodRule;
042 import org.kuali.kpme.tklm.time.service.TkServiceLocator;
043 import org.kuali.kpme.tklm.time.timeblock.TimeBlock;
044 import org.kuali.kpme.tklm.time.timehourdetail.TimeHourDetail;
045 import org.kuali.kpme.tklm.time.timesheet.TimesheetDocument;
046 import org.kuali.kpme.tklm.time.workflow.TimesheetDocumentHeader;
047 import org.kuali.kpme.tklm.utils.TkTestConstants;
048 import org.kuali.rice.krad.service.KRADServiceLocator;
049
050 import com.gargoylesoftware.htmlunit.NicelyResynchronizingAjaxController;
051 import com.gargoylesoftware.htmlunit.WebClient;
052 import com.gargoylesoftware.htmlunit.html.HtmlElement;
053 import com.gargoylesoftware.htmlunit.html.HtmlPage;
054
055
056 @FunctionalTest
057 public 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 }