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}