001    /**
002     * Copyright 2004-2013 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.util;
017    
018    import java.io.File;
019    import java.io.FileOutputStream;
020    import java.io.StringReader;
021    import java.net.URL;
022    import java.util.ArrayList;
023    import java.util.Arrays;
024    import java.util.Iterator;
025    import java.util.List;
026    import java.util.Map;
027    import java.util.Set;
028    
029    import org.apache.log4j.Logger;
030    import org.junit.Assert;
031    import org.kuali.kpme.core.util.HrTestConstants;
032    import org.kuali.rice.core.api.config.property.ConfigContext;
033    import org.w3c.dom.NamedNodeMap;
034    import org.w3c.dom.Node;
035    
036    import com.gargoylesoftware.htmlunit.WebClient;
037    import com.gargoylesoftware.htmlunit.html.HtmlAnchor;
038    import com.gargoylesoftware.htmlunit.html.HtmlCheckBoxInput;
039    import com.gargoylesoftware.htmlunit.html.HtmlElement;
040    import com.gargoylesoftware.htmlunit.html.HtmlFileInput;
041    import com.gargoylesoftware.htmlunit.html.HtmlForm;
042    import com.gargoylesoftware.htmlunit.html.HtmlHiddenInput;
043    import com.gargoylesoftware.htmlunit.html.HtmlInput;
044    import com.gargoylesoftware.htmlunit.html.HtmlPage;
045    import com.gargoylesoftware.htmlunit.html.HtmlRadioButtonInput;
046    import com.gargoylesoftware.htmlunit.html.HtmlSelect;
047    import com.gargoylesoftware.htmlunit.html.HtmlSubmitInput;
048    import com.gargoylesoftware.htmlunit.html.HtmlTextArea;
049    import com.gargoylesoftware.htmlunit.html.HtmlTextInput;
050    
051    public 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) lookupForm.getElementById(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    }