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&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    }