001/**
002 * Copyright 2004-2015 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&currentClockAction=" + 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}