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.common.TkConstants;
040import org.kuali.kpme.tklm.time.clocklog.ClockLog;
041import org.kuali.kpme.tklm.time.rules.graceperiod.GracePeriodRule;
042import org.kuali.kpme.tklm.time.service.TkServiceLocator;
043import org.kuali.kpme.tklm.time.timeblock.TimeBlock;
044import org.kuali.kpme.tklm.time.timehourdetail.TimeHourDetail;
045import org.kuali.kpme.tklm.time.timesheet.TimesheetDocument;
046import org.kuali.kpme.tklm.time.workflow.TimesheetDocumentHeader;
047import org.kuali.kpme.tklm.utils.TkTestConstants;
048import org.kuali.rice.krad.service.KRADServiceLocator;
049
050import com.gargoylesoftware.htmlunit.NicelyResynchronizingAjaxController;
051import com.gargoylesoftware.htmlunit.WebClient;
052import com.gargoylesoftware.htmlunit.html.HtmlElement;
053import com.gargoylesoftware.htmlunit.html.HtmlPage;
054
055
056@FunctionalTest
057public 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&currentClockAction=" + 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}