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