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.util;
017
018import java.io.File;
019import java.io.FileOutputStream;
020import java.io.StringReader;
021import java.net.URL;
022import java.util.ArrayList;
023import java.util.Arrays;
024import java.util.Iterator;
025import java.util.List;
026import java.util.Map;
027import java.util.Set;
028
029import org.apache.log4j.Logger;
030import org.junit.Assert;
031import org.kuali.kpme.core.util.HrTestConstants;
032import org.kuali.rice.core.api.config.property.ConfigContext;
033import org.w3c.dom.NamedNodeMap;
034import org.w3c.dom.Node;
035
036import com.gargoylesoftware.htmlunit.WebClient;
037import com.gargoylesoftware.htmlunit.html.HtmlAnchor;
038import com.gargoylesoftware.htmlunit.html.HtmlCheckBoxInput;
039import com.gargoylesoftware.htmlunit.html.HtmlElement;
040import com.gargoylesoftware.htmlunit.html.HtmlFileInput;
041import com.gargoylesoftware.htmlunit.html.HtmlForm;
042import com.gargoylesoftware.htmlunit.html.HtmlHiddenInput;
043import com.gargoylesoftware.htmlunit.html.HtmlInput;
044import com.gargoylesoftware.htmlunit.html.HtmlPage;
045import com.gargoylesoftware.htmlunit.html.HtmlRadioButtonInput;
046import com.gargoylesoftware.htmlunit.html.HtmlSelect;
047import com.gargoylesoftware.htmlunit.html.HtmlSubmitInput;
048import com.gargoylesoftware.htmlunit.html.HtmlTextArea;
049import com.gargoylesoftware.htmlunit.html.HtmlTextInput;
050
051public class HtmlUnitUtil {
052    private static final Logger LOG = Logger.getLogger(HtmlUnitUtil.class);
053
054    /**
055     * 
056     * @param url
057     * @return htmlpage without js enabled
058     * @throws Exception
059     */
060    public static HtmlPage gotoPageAndLogin(WebClient webClient, String url) throws Exception {
061        return gotoPageAndLogin(webClient, url, false);
062    }
063    
064    /**
065     * 
066     * @param url
067     * @param enableJavascript
068     * @return htmlpage with js enabled
069     * @throws Exception
070     */
071    public static HtmlPage gotoPageAndLogin(WebClient webClient, String url, boolean enableJavascript) throws Exception {
072        LOG.debug("URL: " + url);
073
074        // this is required and needs to set to true, otherwise the values set by the onClick event won't be triggered, e.g. methodToCall
075        webClient.getOptions().setJavaScriptEnabled(enableJavascript);
076        return (HtmlPage) webClient.getPage(new URL(url));
077    }
078
079    public static boolean pageContainsText(HtmlPage page, String text) {
080        return page.asText().indexOf(text) >= 0;
081    }
082
083        /**
084         *
085         * @param page: current html page
086         * @param criteria: The key is the field name and the value is a string array which contains the field value and the field type which can be chosen from TkTestConstants
087         * @return HtmlPage resultPage
088         * @throws Exception
089         */
090        public static HtmlPage fillOutForm(HtmlPage page, Map<String, Object> criteria) throws Exception {
091                HtmlForm lookupForm = HtmlUnitUtil.getDefaultForm(page);
092                String formFieldPrefix = "";
093                HtmlPage resultPage = null;
094                HtmlSelect select = null;
095                HtmlInput input = null;
096                HtmlCheckBoxInput checkBox = null;
097                HtmlTextArea textArea = null;
098
099
100                // Common class of both HtmlInput and HtmlTextArea -- since either of these can appear
101                // on a web-form.
102                HtmlElement htmlBasicInput = null;
103
104                Set<Map.Entry<String, Object>> entries = criteria.entrySet();
105                Iterator<Map.Entry<String, Object>> it = entries.iterator();
106
107                while (it.hasNext()) {
108                        Map.Entry<String,Object> entry = it.next();
109
110                        // if the field type is not <input>
111                        if(entry.getValue() instanceof String[]) {
112                                String key = Arrays.asList((String[])entry.getValue()).get(0).toString();
113                                String value = Arrays.asList((String[])entry.getValue()).get(1).toString();
114
115                                // drop-down
116                                if(key.equals(HrTestConstants.FormElementTypes.DROPDOWN)) {
117
118                                        try {
119                                                select = (HtmlSelect) lookupForm.getSelectByName(formFieldPrefix  + entry.getKey());
120                                        } catch (Exception e) {
121                                                select = (HtmlSelect) page.getHtmlElementById(formFieldPrefix  + entry.getKey());
122                                        }
123                   //  try to get a useful option, other than the typical blank default, by getting the last option
124                   //  if the size of the options list is zero, then there is a problem. might as well error here with an array out of bounds.
125                   resultPage = (HtmlPage) select.getOption(select.getOptionSize()-1).setSelected(true);
126                   HtmlUnitUtil.createTempFile(resultPage);
127               }
128                                // check box
129                                else if(key.equals(HrTestConstants.FormElementTypes.CHECKBOX)) {
130                                        try {
131                                          checkBox = page.getHtmlElementById(formFieldPrefix  + entry.getKey());
132                                        }
133                                        catch(Exception e) {
134                                                checkBox = page.getElementByName(formFieldPrefix  + entry.getKey());
135                                        }
136                                        resultPage = (HtmlPage) checkBox.setChecked(Boolean.parseBoolean(value));
137                                }
138                                // text area
139                                else if(key.equals(HrTestConstants.FormElementTypes.TEXTAREA)) {
140                                        try {
141                                           textArea = page.getHtmlElementById(formFieldPrefix  + entry.getKey());
142                                        } catch (Exception e){
143                                                textArea = page.getElementByName(formFieldPrefix  + entry.getKey());
144                                        }
145                                        textArea.setText(Arrays.asList((String[])entry.getValue()).get(1).toString());
146                                }
147                        } else {
148                                try {
149                                        htmlBasicInput = page.getHtmlElementById(formFieldPrefix + entry.getKey());
150                                        if (htmlBasicInput instanceof HtmlTextArea) {
151                                                textArea = (HtmlTextArea)htmlBasicInput;
152                                                textArea.setText(entry.getValue().toString());
153                                                resultPage = (HtmlPage) textArea.getPage();
154                                        } else if (htmlBasicInput instanceof HtmlInput) {
155                                                input = (HtmlInput)htmlBasicInput;
156                                                resultPage = (HtmlPage) input.setValueAttribute(entry.getValue().toString());
157                                        } else {
158                                                LOG.error("Request to populate a non-input html form field.");
159                                        }
160                                } catch (Exception e) {
161                                        htmlBasicInput = page.getElementByName(formFieldPrefix + entry.getKey());
162
163                                        if (htmlBasicInput instanceof HtmlTextArea) {
164                                                textArea = (HtmlTextArea)htmlBasicInput;
165                                                textArea.setText(entry.getValue().toString());
166                                                resultPage = (HtmlPage) textArea.getPage();
167                                        } else if (htmlBasicInput instanceof HtmlInput) {
168                                                input = (HtmlInput)htmlBasicInput;
169                                                resultPage = (HtmlPage) input.setValueAttribute(entry.getValue().toString());
170                                        } else {
171                                                LOG.error("Request to populate a non-input html form field.");
172                                        }
173                                }
174                        }
175                }
176                HtmlUnitUtil.createTempFile(resultPage);
177                return resultPage;
178        }
179        
180   /**
181    * Method to obtain the HREF onclick='' value from the button when
182    * the client side typically processes the request.
183    * @param button
184    */
185   public static String getOnClickHref(HtmlElement button) {
186       NamedNodeMap attributes = button.getAttributes();
187       Node node = attributes.getNamedItem("onclick");
188
189       //location.href='TimesheetSubmit.do?action=R&documentId=2000&methodToCall=approveTimesheet'
190       String hrefRaw = node.getNodeValue();
191       int sidx = hrefRaw.indexOf("='");
192
193       return hrefRaw.substring(sidx+2, hrefRaw.length() - 1);
194   }
195        /**
196         *
197         * @param page: current html page //NOTE doesnt seem to work currently for js setting of form variables
198         * @param name: the button name
199         * @return
200         * @throws Exception
201         */
202        public static HtmlPage clickButton(HtmlPage page, String name) throws Exception {
203                HtmlForm form = HtmlUnitUtil.getDefaultForm(page);
204                HtmlSubmitInput input = form.getInputByName(name);
205                return (HtmlPage) input.click();
206        }
207        
208        public static HtmlPage clickClockInOrOutButton(HtmlPage page) throws Exception {
209                HtmlForm form = HtmlUnitUtil.getDefaultForm(page);
210                
211                HtmlSubmitInput input = form.getInputByName("clockAction");
212                form.getInputByName("methodToCall").setValueAttribute("clockAction");
213                return (HtmlPage) input.click();
214        }
215        
216        public static HtmlPage clickLunchInOrOutButton(HtmlPage page, String lunchAction) throws Exception {
217                HtmlForm form = HtmlUnitUtil.getDefaultForm(page);
218                
219                HtmlSubmitInput input = null;
220                if(lunchAction.equals("LO")) {
221                        input = form.getInputByName("lunchOut");
222                }
223                else if(lunchAction.equals("LI")) {
224                        input = form.getInputByName("lunchIn");
225                }
226                form.getInputByName("methodToCall").setValueAttribute("clockAction");
227                form.getInputByName("currentClockAction").setValueAttribute(lunchAction);
228                return (HtmlPage) input.click();
229        }
230    
231        public static HtmlPage clickInputContainingText(HtmlPage page, String...values) throws Exception {
232                page = (HtmlPage)getInputContainingText(page, values).click();
233                return page;
234        }
235
236    public static HtmlInput getInputContainingText(HtmlPage page, String... values) throws Exception {
237                List<HtmlForm> forms = page.getForms();
238                for (HtmlForm form : forms){
239                        for(HtmlElement element : form.getHtmlElementDescendants()) {
240                                if (element instanceof HtmlInput) {
241                                        if (elementContainsValues(element, values)) {
242                                                return (HtmlInput)element;
243                                        }
244                                }
245                        }
246                }
247                return null;
248        }
249
250        public final static void setFieldValue(HtmlPage page, String fieldId, String fieldValue) {
251            HtmlElement element = page.getHtmlElementById(fieldId);
252            Assert.assertTrue("element " + fieldId + " is null, page: " + page.asText(), element != null);
253        
254            if (element instanceof HtmlTextInput) {
255                HtmlTextInput textField = (HtmlTextInput) element;
256                textField.setValueAttribute(fieldValue);
257            } else if (element instanceof HtmlTextArea) {
258                HtmlTextArea textAreaField = (HtmlTextArea) element;
259                textAreaField.setText(fieldValue);
260            } else if (element instanceof HtmlHiddenInput) {
261                HtmlHiddenInput hiddenField = (HtmlHiddenInput) element;
262                hiddenField.setValueAttribute(fieldValue);
263            } else if (element instanceof HtmlSelect) {
264                HtmlSelect selectField = (HtmlSelect) element;
265                try {
266                    selectField.setSelectedAttribute(fieldValue, true);
267                } catch (IllegalArgumentException e) {
268                    Assert.fail("select element [" + element.asText() + "] " + e.getMessage());
269                }
270            } else if (element instanceof HtmlCheckBoxInput) {
271                HtmlCheckBoxInput checkboxField = (HtmlCheckBoxInput) element;
272                if (fieldValue.equals("on")) {
273                    checkboxField.setChecked(true);
274                } else if (fieldValue.equals("off")) {
275                    checkboxField.setChecked(false);
276                } else {
277                        Assert.assertTrue("Invalid checkbox value", false);
278                }
279            } else if (element instanceof HtmlFileInput) {
280                HtmlFileInput fileInputField = (HtmlFileInput) element;
281                fileInputField.setValueAttribute(fieldValue);
282            } else if (element instanceof HtmlRadioButtonInput) {
283                HtmlRadioButtonInput radioButton = (HtmlRadioButtonInput) element;
284                if (fieldValue.equals("on")) {
285                        radioButton.setChecked(true);
286                } else if (fieldValue.equals("off")) {
287                        radioButton.setChecked(false);
288                }
289            } else {
290                Assert.fail("Unknown control field: " + fieldId);
291            }
292        }
293    
294    
295    public static List<HtmlInput> getInputsContainingText(HtmlPage page, String... values) throws Exception {
296                List<HtmlInput> inputs = new ArrayList<HtmlInput>();
297                List<HtmlForm> forms = page.getForms();
298                for (HtmlForm form : forms){
299
300                        for(HtmlElement element : form.getHtmlElementDescendants()) {
301                                if (element instanceof HtmlInput) {
302                                        if (elementContainsValues(element, values)) {
303                                                inputs.add((HtmlInput)element);
304                                        }
305                                }
306                        }
307                }
308                return inputs;
309        }
310
311        protected static boolean elementContainsValues(HtmlElement element, String... values) {
312                for (String value : values) {
313                        if (element.toString().indexOf(value) == -1) {
314                                return false;
315                        }
316        }
317                return true;
318        }
319
320        public static HtmlPage clickAnchorContainingText(HtmlPage page, String... values) throws Exception {
321                return (HtmlPage) getAnchorContainingText(page, values).click();
322        }
323
324        @SuppressWarnings("unchecked")
325        public static HtmlAnchor getAnchorContainingText(HtmlPage page, String... values) throws Exception {
326                for (Iterator iterator = page.getAnchors().iterator(); iterator.hasNext();) {
327                        HtmlAnchor anchor = (HtmlAnchor) iterator.next();
328                        if (elementContainsValues(anchor, values)) {
329                                return anchor;
330                        }
331                }
332                return null;
333        }
334
335    public static void createTempFile(HtmlPage page) throws Exception {
336        createTempFile(page, null);
337    }
338
339    public static void createTempFile(HtmlPage page, String name) throws Exception {
340        name = name == null ? "TestOutput" : name;
341        File temp = File.createTempFile(name, ".html", new File(ConfigContext.getCurrentContextConfig().getProperty("temp.dir")));
342        FileOutputStream fos = new FileOutputStream(temp);
343        String xml = page.asXml();
344        StringReader xmlReader = new StringReader(xml);
345        int i;
346        while ((i = xmlReader.read()) != -1) {
347            fos.write(i);
348        }
349        try {
350            fos.close();
351        } catch (Exception e) {
352        }
353        try {
354            xmlReader.close();
355        } catch (Exception e) {
356        }
357    }
358
359    public static HtmlInput getInputContainingText(HtmlForm form, String text) throws Exception {
360
361                for (HtmlElement element : form.getHtmlElementDescendants()) {
362                        if (element instanceof HtmlInput) {
363                                HtmlInput i = (HtmlInput) element;
364                                if (element.toString().contains(text)) {
365                                        return i;
366                                }
367                        }
368
369                }
370                return null;
371    }
372    
373        public static HtmlForm getDefaultForm(HtmlPage htmlPage) {
374                if (htmlPage.getForms().size() == 1) {
375                        return (HtmlForm)htmlPage.getForms().get(0);
376                }
377                return (HtmlForm)htmlPage.getForms().get(1);
378        }
379
380}