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 }