1 /*
2 * Copyright 2007 The Kuali Foundation
3 *
4 * Licensed under the Educational Community License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.opensource.org/licenses/ecl2.php
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 package org.kuali.rice.web.test;
17
18 import java.io.IOException;
19 import java.net.URL;
20 import java.util.ArrayList;
21 import java.util.Iterator;
22 import java.util.List;
23
24 import org.apache.jasper.tagplugins.jstl.core.Url;
25 import org.apache.log4j.Logger;
26 import org.junit.After;
27 import org.junit.Before;
28 import org.kuali.rice.kns.UserSession;
29 import org.kuali.rice.kns.service.DocumentService;
30 import org.kuali.rice.kns.service.KNSServiceLocator;
31 import org.kuali.rice.kns.util.GlobalVariables;
32 import org.kuali.rice.test.web.HtmlUnitUtil;
33
34 import com.gargoylesoftware.htmlunit.ElementNotFoundException;
35 import com.gargoylesoftware.htmlunit.Page;
36 import com.gargoylesoftware.htmlunit.WebClient;
37 import com.gargoylesoftware.htmlunit.html.BaseFrame;
38 import com.gargoylesoftware.htmlunit.html.ClickableElement;
39 import com.gargoylesoftware.htmlunit.html.DomNode;
40 import com.gargoylesoftware.htmlunit.html.FrameWindow;
41 import com.gargoylesoftware.htmlunit.html.HtmlAnchor;
42 import com.gargoylesoftware.htmlunit.html.HtmlCheckBoxInput;
43 import com.gargoylesoftware.htmlunit.html.HtmlElement;
44 import com.gargoylesoftware.htmlunit.html.HtmlFileInput;
45 import com.gargoylesoftware.htmlunit.html.HtmlForm;
46 import com.gargoylesoftware.htmlunit.html.HtmlHiddenInput;
47 import com.gargoylesoftware.htmlunit.html.HtmlImageInput;
48 import com.gargoylesoftware.htmlunit.html.HtmlInlineFrame;
49 import com.gargoylesoftware.htmlunit.html.HtmlListItem;
50 import com.gargoylesoftware.htmlunit.html.HtmlOption;
51 import com.gargoylesoftware.htmlunit.html.HtmlPage;
52 import com.gargoylesoftware.htmlunit.html.HtmlRadioButtonInput;
53 import com.gargoylesoftware.htmlunit.html.HtmlSelect;
54 import com.gargoylesoftware.htmlunit.html.HtmlSubmitInput;
55 import com.gargoylesoftware.htmlunit.html.HtmlTable;
56 import com.gargoylesoftware.htmlunit.html.HtmlTableBody;
57 import com.gargoylesoftware.htmlunit.html.HtmlTableCell;
58 import com.gargoylesoftware.htmlunit.html.HtmlTableRow;
59 import com.gargoylesoftware.htmlunit.html.HtmlTextArea;
60 import com.gargoylesoftware.htmlunit.html.HtmlTextInput;
61
62 /**
63 * This is base class for HtmlUnit tests based on work Lin-Long Shyu originally did for KRA and
64 * that Don Barre enhanced.
65 */
66 public abstract class WebTestBase extends ServerTestBase {
67 private static final Logger LOG = Logger.getLogger(WebTestBase.class);
68
69 protected static String HELP_PAGE_TITLE = "Kuali :: Kuali Help";
70 protected static String USER_NETWORK_ID = "admin";
71
72 protected WebClient webClient = null;
73 protected DocumentService documentService = null;
74
75 private HtmlPage portalPage;
76
77 /**
78 * Web test setup overloading. Sets up Portal page access.
79 *
80 * @see org.kuali.rice.web.test.WebTestBase#setUp()
81 */
82 @Before
83 public void setUp() throws Exception {
84 super.setUp();
85 GlobalVariables.setUserSession(new UserSession(USER_NETWORK_ID));
86 documentService = KNSServiceLocator.getDocumentService();
87 webClient = new WebClient();
88
89 setPortalPage(buildPageFromUrl(HtmlUnitUtil.BASE_URL, HTML_PAGE_TITLE_TEXT));
90
91 }
92
93 /**
94 * Web test tear down overloading.
95 *
96 * @see org.kuali.rice.web.test.WebTestBase#tearDown()
97 */
98 @After
99 public void tearDown() throws Exception {
100 super.tearDown();
101 GlobalVariables.setUserSession(null);
102 documentService = null;
103 webClient = null;
104 }
105
106 /**
107 * Set the KRA Portal Web Page. The portal page is the starting point
108 * for many (or all) HtmlUnit tests in order to simulate a user.
109 *
110 * @param portalPage <code>{@link HtmlPage}</code> instance for the portal page
111 */
112 protected final void setPortalPage(HtmlPage portalPage) {
113 this.portalPage = portalPage;
114 }
115
116 /**
117 * Get the KRA Portal Web Page. The portal page is the starting point
118 * for many (or all) HtmlUnit tests in order to simulate a user.
119 * @return the KRA Portal Web Page.
120 * @throws IOException
121 */
122 protected final HtmlPage getPortalPage() throws IOException {
123 return this.portalPage;
124 }
125
126 /**
127 * Take a given <code>{@link String}</code> url and get a <code>{@link HtmlPage}</code> instance
128 * from it. This method actually overloads <code>{@link #buildPageFromUrl(URL, String)}</code>
129 *
130 * @param url <code>{@link String}</code> instance of url
131 * @param title to compare against and verify the page is valid
132 * @return HtmlPage
133 * @throws IOException
134 */
135 protected final HtmlPage buildPageFromUrl(String url, String title) throws IOException {
136 return buildPageFromUrl(new URL(url), title);
137 }
138
139 /**
140 * Take a given <code>{@link URL}</code> url and get a <code>{@link HtmlPage}</code> instance
141 * from it.
142 *
143 * @param url <code>{@link Url}</code> instance of url
144 * @param title to compare against and verify the page is valid
145 * @return HtmlPage
146 * @throws IOException
147 */
148 protected final HtmlPage buildPageFromUrl(URL url, String title) throws IOException {
149 HtmlPage retval = (HtmlPage) webClient.getPage(url);
150 assertEquals(title, retval.getTitleText());
151 return retval;
152 }
153
154 /**
155 * Simulate clicking on an HTML element in the web page. To find
156 * the HTML element, the following algorithm is used:
157 * <ol>
158 * <li>Search for a HTML element with an <b>id</b> attribute that matches the given id.</li>
159 * <li>If not found, search for the first HTML element with a <b>name</b> attribute that matches.</li>
160 * <li>If not found, search for the first HTML element with a <b>title</b> attribute that matches.</li>
161 * </ol>
162 * If an HTML element is not found or the element is not clickable,
163 * an assertion will cause the test case to fail.
164 *
165 * Using any of the <b>clickOn()</b> methods is the preferred way to click on an HTML element
166 * due to the Login process. If the Login web page is encountered, the user will be
167 * automatically logged in and next web page is returned.
168 *
169 * @param page the HTML web page.
170 * @param id the <i>id</i> of the HTML element to click on.
171 * @return the next web page after clicking on the HTML element.
172 * @throws IOException
173 */
174 protected final HtmlPage clickOn(HtmlPage page, String id) throws IOException {
175 return clickOn(page, id, null);
176 }
177
178 /**
179 * Simulate clicking on an HTML element in the web page. To find
180 * the HTML element, the following algorithm is used:
181 * <ol>
182 * <li>Search for a HTML element with an <b>id</b> attribute that matches the given id.</li>
183 * <li>If not found, search for the first HTML element with a <b>name</b> attribute that matches.</li>
184 * <li>If not found, search for the first HTML element with a <b>title</b> attribute that matches.</li>
185 * </ol>
186 * If an HTML element is not found or the element is not clickable,
187 * an assertion will cause the test case to fail. Also, if the <i>nextPageTitle</i>
188 * is not null, the test case will fail if the next web page doesn't have the
189 * expected title.
190 *
191 * Using any of the <b>clickOn()</b> methods is the preferred way to click on an HTML element
192 * due to the Login process. If the Login web page is encountered, the user will be
193 * automatically logged in and next web page is returned.
194 *
195 * @param page the HTML web page.
196 * @param id the <i>id</i> of the HTML element to click on.
197 * @param nextPageTitle the expected title of the new web page (may be null).
198 * @return the next web page after clicking on the HTML element.
199 * @throws IOException
200 */
201 protected final HtmlPage clickOn(HtmlPage page, String id, String nextPageTitle) throws IOException {
202 HtmlElement element = getElement(page, id);
203 assertTrue(id +" not found",element != null);
204 assertTrue(element instanceof ClickableElement);
205
206 return clickOn((ClickableElement) element, nextPageTitle);
207 }
208
209 /**
210 * Simulate clicking on an HTML element in the web page.
211 *
212 * Using any of the <b>clickOn()</b> methods is the preferred way to click on an HTML element
213 * due to the Login process. If the Login web page is encountered, the user will be
214 * automatically logged in and next web page is returned.
215 *
216 * @param element the HTML element to click on.
217 * @return the next web page after clicking on the HTML element.
218 * @throws IOException
219 */
220 protected final HtmlPage clickOn(HtmlElement element) throws IOException {
221 return clickOn(element, null);
222 }
223
224 /**
225 * Simulate clicking on an HTML element in the web page.
226 *
227 * Using any of the <b>clickOn()</b> methods is the preferred way to click on an HTML element
228 * due to the Login process. If the Login web page is encountered, the user will be
229 * automatically logged in and next web page is returned.
230 *
231 * If the <i>nextPageTitle</i> is not null, the test case will fail if the next web page
232 * doesn't have the expected title.
233 *
234 * @param element the HTML element to click on.
235 * @param nextPageTitle the expected title of the new web page (may be null).
236 * @return the next web page after clicking on the HTML element.
237 * @throws IOException
238 */
239 protected final HtmlPage clickOn(HtmlElement element, String nextPageTitle) throws IOException {
240 assertTrue(element instanceof ClickableElement);
241
242 ClickableElement clickable = (ClickableElement) element;
243 Page nextPage = clickable.click();
244
245 assertTrue(nextPage != null);
246 assertTrue(nextPage instanceof HtmlPage);
247
248 HtmlPage htmlNextPage = (HtmlPage) nextPage;
249
250 htmlNextPage = checkForLoginPage(htmlNextPage);
251
252 if (nextPageTitle != null) {
253 assertEquals(nextPageTitle, htmlNextPage.getTitleText());
254 }
255
256 return htmlNextPage;
257 }
258
259 /**
260 * Simulate clicking on a Lookup icon.
261 * This method is similar to the <b>clickOn()</b> methods except that it
262 * is used for clicking on Lookup icon. This is because it is difficult to
263 * find a Lookup HTML element based upon its full name, i.e. the name is
264 * incredibly long and cryptic. To find the Lookup HTML element, the name
265 * attribute of the HTML lookup elements are examined to see if they contain
266 * the given <i>id</i>. As soon as a match is found, that HTML element
267 * is clicked on. Users should be sure to pick a part of the Lookup's name
268 * that is unique for that Lookup.
269 *
270 * If the Lookup HTML element is not found, an assertion will cause
271 * the test to fail.
272 *
273 * @param page the HTML web page.
274 * @param tag identifies the Lookup HTML element.
275 * @return the Lookup web page.
276 * @throws IOException
277 */
278 protected final HtmlPage clickOnLookup(HtmlPage page, String tag) throws IOException {
279 HtmlImageInput element = getLookup(page, tag);
280 assertTrue(element != null);
281
282 return clickOn(element);
283 }
284
285 /**
286 * Asserts that the given web page contains the given text.
287 * @param page the HTML web page.
288 * @param text the string to look for in the web page.
289 */
290 protected final void assertContains(HtmlPage page, String text) {
291 assertTrue(page.asText().contains(text));
292 }
293
294 /**
295 * Asserts that the given web page does <b>not</b> contain the given text.
296 * @param page the HTML web page.
297 * @param text the string to look for in the web page.
298 */
299 protected final void assertDoesNotContain(HtmlPage page, String text) {
300 assertTrue(!page.asText().contains(text));
301 }
302
303 /**
304 * Asserts that the given HTML element contains the given text.
305 * @param element the HTML element.
306 * @param text the string to look for in the HTML element.
307 */
308 protected final void assertContains(HtmlElement element, String text) {
309 assertTrue(element.asText().contains(text));
310 }
311
312 /**
313 * Asserts that the given HTML element does <b>not</b> contain the given text.
314 * @param element the HTML element.
315 * @param text the string to look for in the HTML element.
316 */
317 protected final void assertDoesNotContain(HtmlElement element, String text) {
318 assertTrue(!element.asText().contains(text));
319 }
320
321 /**
322 * Asserts that a Select control has the given number of options.
323 * @param page the HTML web page.
324 * @param elementId the value of the HTML element's id attribute.
325 * @param size the number of options that must be in the list of options.
326 */
327 protected final void assertSelectOptionsSize(HtmlPage page, String elementId, int size) {
328 HtmlElement element = page.getHtmlElementById(elementId);
329 assertTrue(element != null);
330
331 if (element instanceof HtmlSelect) {
332 HtmlSelect selectField = (HtmlSelect) element;
333 assertEquals(selectField.getOptionSize(), size);
334 }
335 else {
336 assertTrue("Not a Select Field", false);
337 }
338 }
339
340 /**
341 * Performs a single value Lookup. The following occurs on a lookup:
342 * <ol>
343 * <li>The Lookup icon is clicked on.</li>
344 * <li>In the Lookup web page, the search button is clicked on.</li>
345 * <li>The first item in the results is returned.</li>
346 * <li>The web page resulting from clicking on "return value" is returned.</li>
347 * </ol>
348 * To find the Lookup HTML element, the name attribute of the HTML lookup
349 * elements are examined to see if they contain the given <i>id</i>. As soon as
350 * a match is found, that HTML element is clicked on. Users should be sure to pick
351 * a part of the Lookup's name that is unique for that Lookup.
352 *
353 * The test will fail for any of the following reasons:
354 * <ul>
355 * <li>The HTML lookup element was not found.</li>
356 * <li>There was no data returned in the search.</li>
357 * </ul>
358 *
359 * @param page the original web page with the Lookup icon.
360 * @param tag identifies the Lookup icon to click on.
361 * @return the resulting web page.
362 * @throws IOException
363 */
364 protected final HtmlPage lookup(HtmlPage page, String tag) throws IOException {
365 return lookup(page, tag, null, null);
366 }
367
368 /**
369 * Performs a single value Lookup. The following occurs on a lookup:
370 * <ol>
371 * <li>The Lookup icon is clicked on.</li>
372 * <li>In the Lookup web page, the given field is filled in with the given value.</li>
373 * <li>In the Lookup web page, the search button is clicked on.</li>
374 * <li>The first item in the results is returned.</li>
375 * <li>The web page resulting from clicking on "return value" is returned.</li>
376 * </ol>
377 * To find the Lookup HTML element, the name attribute of the HTML lookup
378 * elements are examined to see if they contain the given <i>id</i>. As soon as
379 * a match is found, that HTML element is clicked on. Users should be sure to pick
380 * a part of the Lookup's name that is unique for that Lookup.
381 *
382 * The test will fail for any of the following reasons:
383 * <ul>
384 * <li>The HTML lookup element was not found.</li>
385 * <li>The search field HTML element was not found.</li>
386 * <li>There was no data returned in the search.</li>
387 * </ul>
388 *
389 * @param page the original web page with the Lookup icon.
390 * @param tag identifies the Lookup icon to click on.
391 * @param searchFieldId the id of the HTML field element (may be null).
392 * @param searchValue the value to insert into the search field (may be null if searchFieldId is null).
393 * @return the resulting web page.
394 * @throws IOException
395 */
396 protected final HtmlPage lookup(HtmlPage page, String tag, String searchFieldId, String searchValue) throws IOException {
397
398 HtmlPage lookupPage = clickOnLookup(page, tag);
399
400 if (searchFieldId != null) {
401 assertTrue(searchValue != null);
402 setFieldValue(lookupPage, searchFieldId, searchValue);
403 }
404
405 // click on the search button
406 HtmlImageInput searchBtn = (HtmlImageInput) getElement(lookupPage, "methodToCall.search", "search", "search");
407 HtmlPage resultsPage = (HtmlPage) searchBtn.click();
408
409 HtmlTable table = (HtmlTable) getElement(resultsPage, "row");
410 assertTrue("No data to return", table != null);
411
412 HtmlTableBody body = (HtmlTableBody) table.getBodies().get(0);
413 List rows = body.getRows();
414
415 HtmlTableRow row = (HtmlTableRow) rows.get(0);
416 List cells = row.getCells();
417 HtmlTableCell cell = (HtmlTableCell) cells.get(cells.size() - 1);
418 HtmlAnchor anchor = (HtmlAnchor) getFirstChild(cell);
419 page = (HtmlPage) anchor.click();
420
421 return page;
422 }
423
424 /**
425 * Performs a multi value Lookup. The following occurs on a lookup:
426 * <ol>
427 * <li>The Lookup icon is clicked on.</li>
428 * <li>In the Lookup web page, the search button is clicked on.</li>
429 * <li>The "select all" button is clicked on.</li>
430 * <li>The web page resulting from clicking on "return selected" is returned.</li>
431 * </ol>
432 * To find the Lookup HTML element, the name attribute of the HTML lookup
433 * elements are examined to see if they contain the given <i>id</i>. As soon as
434 * a match is found, that HTML element is clicked on. Users should be sure to pick
435 * a part of the Lookup's name that is unique for that Lookup.
436 *
437 * The test will fail if the Lookup HTML element is not found.
438 *
439 * @param page the original web page with the Lookup icon.
440 * @param tag identifies the Lookup icon to click on.
441 * @return the resulting web page.
442 * @throws IOException
443 */
444 protected final HtmlPage multiLookup(HtmlPage page, String tag) throws IOException {
445 return multiLookup(page, tag, null, null);
446 }
447
448 /**
449 * Performs a multi value Lookup. The following occurs on a lookup:
450 * <ol>
451 * <li>The Lookup icon is clicked on.</li>
452 * <li>The search field is filled in with the given search value.</li>
453 * <li>In the Lookup web page, the search button is clicked on.</li>
454 * <li>The "select all" button is clicked on.</li>
455 * <li>The web page resulting from clicking on "return selected" is returned.</li>
456 * </ol>
457 * To find the Lookup HTML element, the name attribute of the HTML lookup
458 * elements are examined to see if they contain the given <i>id</i>. As soon as
459 * a match is found, that HTML element is clicked on. Users should be sure to pick
460 * a part of the Lookup's name that is unique for that Lookup.
461 *
462 * The test will fail for any of the following reasons:
463 * <ul>
464 * <li>The HTML lookup element was not found.</li>
465 * <li>The search field HTML element was not found.</li>
466 * </ul>
467 *
468 * @param page the original web page with the Lookup icon.
469 * @param tag identifies the Lookup icon to click on.
470 * @param searchFieldId the id of the HTML field element (may be null).
471 * @param searchValue the value to insert into the search field (may be null if searchFieldId is null).
472 * @return the resulting web page.
473 * @throws IOException
474 */
475 protected final HtmlPage multiLookup(HtmlPage page, String tag, String searchFieldId, String searchValue) throws IOException {
476 HtmlPage lookupPage = clickOnLookup(page, tag);
477
478 if (searchFieldId != null) {
479 assertTrue(searchValue != null);
480 setFieldValue(lookupPage, searchFieldId, searchValue);
481 }
482
483 // click on the search button
484 HtmlImageInput searchBtn = (HtmlImageInput) getElement(lookupPage, "methodToCall.search", "search", "search");
485 HtmlPage resultsPage = (HtmlPage) searchBtn.click();
486
487 HtmlImageInput selectAllBtn = (HtmlImageInput) getElement(resultsPage, "methodToCall.selectAll.(::;false;::).x", null, null);
488 HtmlPage selectedPage = (HtmlPage) selectAllBtn.click();
489
490 HtmlImageInput returnAllBtn = (HtmlImageInput) getElement(selectedPage, "methodToCall.prepareToReturnSelectedResults.x", null, null);
491 HtmlPage returnPage = (HtmlPage) returnAllBtn.click();
492
493 return returnPage;
494 }
495
496 /**
497 * Set the value of a control field. The control field must be a
498 * text, text area, hidden, checkbox, radio button, or single-select field.
499 *
500 * For a checkbox field, the only legal values are "on" and "off".
501 *
502 * The test will fail for any of the following reasons:
503 * <ul>
504 * <li>The HTML control element was not found.</li>
505 * <li>The control is not a text, text area, hidden, checkbox, radio button, or select field.</li>
506 * </ul>
507 *
508 * @param page the HTML web page.
509 * @param fieldId the id of the HTML element.
510 * @param fieldValue the value to set the control to.
511 */
512 protected final void setFieldValue(HtmlPage page, String fieldId, String fieldValue) {
513 HtmlElement element = getElement(page, fieldId);
514 assertTrue(element != null);
515
516 if (element instanceof HtmlTextInput) {
517 HtmlTextInput textField = (HtmlTextInput) element;
518 textField.setValueAttribute(fieldValue);
519 }
520 else if (element instanceof HtmlTextArea) {
521 HtmlTextArea textAreaField = (HtmlTextArea) element;
522 textAreaField.setText(fieldValue);
523 }
524 else if (element instanceof HtmlHiddenInput) {
525 HtmlHiddenInput hiddenField = (HtmlHiddenInput) element;
526 hiddenField.setValueAttribute(fieldValue);
527 }
528 else if (element instanceof HtmlSelect) {
529 HtmlSelect selectField = (HtmlSelect) element;
530 selectField.setSelectedAttribute(fieldValue, true);
531 }
532 else if (element instanceof HtmlCheckBoxInput) {
533 HtmlCheckBoxInput checkboxField = (HtmlCheckBoxInput) element;
534 if (fieldValue.equals("on")) {
535 checkboxField.setChecked(true);
536 }
537 else if (fieldValue.equals("off")) {
538 checkboxField.setChecked(false);
539 }
540 else {
541 assertTrue("Invalid checkbox value", false);
542 }
543 }
544 else if (element instanceof HtmlFileInput) {
545 HtmlFileInput fileInputField = (HtmlFileInput) element;
546 fileInputField.setValueAttribute(fieldValue);
547 }
548 else if (element instanceof HtmlRadioButtonInput) {
549 List<HtmlElement> elements = getAllElementsByName(page, fieldId, false);
550 for (HtmlElement child : elements) {
551 assertTrue(child instanceof HtmlRadioButtonInput);
552 HtmlRadioButtonInput radioBtn = (HtmlRadioButtonInput) child;
553 if (radioBtn.getValueAttribute().equals(fieldValue)) {
554 radioBtn.setChecked(true);
555 break;
556 }
557 }
558 }
559 else {
560 assertTrue("Unknown control field", false);
561 }
562 }
563
564 /**
565 * Gets the current value of a control field.
566 *
567 * For a checkbox field, the only legal values are "on" and "off".
568 *
569 * The test will fail for any of the following reasons:
570 * <ul>
571 * <li>The HTML control element was not found.</li>
572 * <li>The control is not a text, text area, hidden, checkbox, radio button, or select field.</li>
573 * </ul>
574 *
575 * @param page the HTML web page.
576 * @param fieldId the id of the HTML element.
577 * @return the current value.
578 */
579 protected final String getFieldValue(HtmlPage page, String fieldId) {
580 String fieldValue = null;
581
582 HtmlElement element = getElement(page, fieldId);
583 assertTrue(fieldId + " not found", element != null);
584
585 if (element instanceof HtmlTextInput) {
586 HtmlTextInput textField = (HtmlTextInput) element;
587 fieldValue = textField.getValueAttribute();
588 }
589 else if (element instanceof HtmlTextArea) {
590 HtmlTextArea textAreaField = (HtmlTextArea) element;
591 fieldValue = textAreaField.getText();
592 }
593 else if (element instanceof HtmlHiddenInput) {
594 HtmlHiddenInput hiddenField = (HtmlHiddenInput) element;
595 fieldValue = hiddenField.getValueAttribute();
596 }
597 else if (element instanceof HtmlSelect) {
598 HtmlSelect selectField = (HtmlSelect) element;
599 fieldValue = ((HtmlOption) selectField.getSelectedOptions().get(0)).getValueAttribute();
600 }
601 else if (element instanceof HtmlCheckBoxInput) {
602 HtmlCheckBoxInput checkboxField = (HtmlCheckBoxInput) element;
603 fieldValue = checkboxField.isChecked() ? "on" : "off";
604 }
605 else if (element instanceof HtmlRadioButtonInput) {
606 List<HtmlElement> elements = getAllElementsByName(page, fieldId, false);
607 for (HtmlElement child : elements) {
608 assertTrue(child instanceof HtmlRadioButtonInput);
609 HtmlRadioButtonInput radioBtn = (HtmlRadioButtonInput) child;
610 if (radioBtn.isChecked()) {
611 fieldValue = radioBtn.getValueAttribute();
612 break;
613 }
614 }
615 }
616 else {
617 assertTrue("Unknown control field", false);
618 }
619
620 return fieldValue;
621 }
622
623 /**
624 * Gets the default value of a control field.
625 *
626 * For a checkbox field, the only legal values are "on" and "off".
627 *
628 * The test will fail for any of the following reasons:
629 * <ul>
630 * <li>The HTML control element was not found.</li>
631 * <li>The control is not a text, text area, hidden, checkbox, radio button, or select field.</li>
632 * </ul>
633 *
634 * @param page the HTML web page.
635 * @param fieldId the id of the HTML element.
636 * @return the default value.
637 */
638 protected final String getDefaultFieldValue(HtmlPage page, String fieldId) {
639 String fieldValue = null;
640
641 HtmlElement element = getElement(page, fieldId);
642 assertTrue(element != null);
643
644 if (element instanceof HtmlTextInput) {
645 HtmlTextInput textField = (HtmlTextInput) element;
646 fieldValue = textField.getDefaultValue();
647 }
648 else if (element instanceof HtmlTextArea) {
649 HtmlTextArea textAreaField = (HtmlTextArea) element;
650 fieldValue = textAreaField.getDefaultValue();
651 }
652 else if (element instanceof HtmlHiddenInput) {
653 HtmlHiddenInput hiddenField = (HtmlHiddenInput) element;
654 fieldValue = hiddenField.getDefaultValue();
655 }
656 else if (element instanceof HtmlSelect) {
657 HtmlSelect selectField = (HtmlSelect) element;
658 fieldValue = selectField.getDefaultValue();
659 }
660 else if (element instanceof HtmlCheckBoxInput) {
661 HtmlCheckBoxInput checkboxField = (HtmlCheckBoxInput) element;
662 fieldValue = checkboxField.isDefaultChecked() ? "on" : "off";
663 }
664 else if (element instanceof HtmlRadioButtonInput) {
665 List<HtmlElement> elements = getAllElementsByName(page, fieldId, false);
666 for (HtmlElement child : elements) {
667 assertTrue(child instanceof HtmlRadioButtonInput);
668 HtmlRadioButtonInput radioBtn = (HtmlRadioButtonInput) child;
669 if (radioBtn.isDefaultChecked()) {
670 fieldValue = radioBtn.getValueAttribute();
671 break;
672 }
673 }
674 }
675 else {
676 assertTrue("Unknown control field", false);
677 }
678
679 return fieldValue;
680 }
681
682 /**
683 * Checks all of the Help hyperlinks on a web page. Each hyperlink
684 * with a <b>helpWindow</b> target is clicked on to verify that the
685 * the Help web page is displayed. The contents of the help page is
686 * not examined. The test will only fail if a Help hyperlink does
687 * not bring up a Help web page. This is useful to verify that there
688 * are no broken links.
689 *
690 * @param page the HTML page to check.
691 * @throws IOException
692 */
693 protected final void checkHelpLinks(HtmlPage page) throws IOException {
694 List<HtmlAnchor> anchors = findHelpLinks(page);
695 for (HtmlAnchor anchor : anchors) {
696 HtmlPage helpPage = (HtmlPage) anchor.click();
697 assertTrue(HELP_PAGE_TITLE.equals(helpPage.getTitleText()));
698 }
699 }
700
701 /**
702 * Checks the Expanded Text Area. Many text area controls have a corresponding
703 * expanded text area icon (pencil) for displaying another web page with a larger
704 * text area box. This method does the following to verify that the expanded text
705 * area feature is working properly.
706 * <ol>
707 * <li>The text area is set to the <i>text1</i> value.</li>
708 * <li>The Expanded Text Area icon is clicked on.</li>
709 * <li>The text in the expanded text area web page is examined to verify
710 * that it is equal to <i>text1</i>.</li>
711 * <li>The text area is changed to <i>text2</i>.</li>
712 * <li>The "save" button is clicked on.</li>
713 * <li>The resulting web page is examined to verify that the original
714 * text area has changed to the value of <i>text2</i>.</li>
715 * </ol>
716 *
717 * The test will fail for any of the following reasons:
718 * <ul>
719 * <li>The HTML text area element was not found.</li>
720 * <li>The Expanded Text Area icon HTML element was not found.</li>
721 * <li>The setting of <i>text1</i> did not transfer to the Expanded Text Area web page.</li>
722 * <li>The saving of <i>text2</i> did not transfer to the original text area.</li>
723 * </ul>
724 *
725 * @param page the HTML web page with the text area control.
726 * @param id identifies the text area (not its corresponding icon).
727 * @param text1 the string to set the original text area to.
728 * @param text2 the string to set in the Expanded Text Area web page.
729 * @return the resulting web page from saving <i>text2</i>.
730 * @throws IOException
731 */
732 protected final HtmlPage checkExpandedTextArea(HtmlPage page, String id, String text1, String text2) throws IOException {
733 boolean javascriptEnabled = webClient.isJavaScriptEnabled();
734
735 webClient.setJavaScriptEnabled(false);
736
737 setFieldValue(page, id, text1);
738
739 HtmlElement field = getElement(page, id);
740 assertTrue(field != null);
741
742 ClickableElement btn = (ClickableElement) this.getNextSibling(field);
743 assertTrue(btn != null);
744
745 HtmlPage textPage = clickOn(btn);
746
747 assertEquals(getFieldValue(textPage, id), text1);
748
749 setFieldValue(textPage, id, text2);
750 HtmlPage returnPage = clickOn(textPage, "save");
751
752 assertEquals(getFieldValue(returnPage, id), text2);
753
754 webClient.setJavaScriptEnabled(javascriptEnabled);
755
756 return returnPage;
757 }
758
759 /**
760 * Gets the error messages for a specific panel. If an operation
761 * results in an error, those errors are displayed in specific panel,
762 * i.e. tab. Each panel is contained within an HTML div tag that has
763 * a unique id, i.e. the HTML id attribute.
764 *
765 * @param page the HTML web page.
766 * @param panelId the unique id of the panel.
767 * @return the list of error strings (may be empty).
768 */
769 protected final List<String> getErrors(HtmlPage page, String panelId) {
770 List<String> errors = new ArrayList<String>();
771
772 HtmlElement panelDiv = getElement(page, panelId);
773 assertTrue(panelDiv != null);
774
775 HtmlElement errorDiv = getElementByClass(panelDiv, "error");
776 if (errorDiv != null) {
777 Iterator iterator = errorDiv.getAllHtmlChildElements();
778 while (iterator.hasNext()) {
779 HtmlElement child = (HtmlElement) iterator.next();
780 if (child instanceof HtmlListItem) {
781 HtmlListItem li = (HtmlListItem) child;
782 errors.add(li.asText());
783 }
784 }
785 }
786
787 return errors;
788 }
789
790 /**
791 * Determines if any of the errors contains the given text string.
792 *
793 * @param errors the list of errors.
794 * @param text the string to compare against.
795 * @return true if any of errors contains the text string; otherwise false.
796 */
797 protected final boolean containsError(List<String> errors, String text) {
798 for (String error : errors) {
799 if (error.contains(text)) {
800 return true;
801 }
802 }
803 return false;
804 }
805
806 /**
807 * Checks for the Login web page. The Login web page can be
808 * obtained due to a request for another web page. This method
809 * logs the user into the system and returns the expected web page.
810 *
811 * @param page the HTML web page.
812 * @return the same HTML web page or the one resulting from the login.
813 * @throws IOException
814 */
815 private HtmlPage checkForLoginPage(HtmlPage page) throws IOException {
816 if (page.getTitleText().equals("Login")) {
817 HtmlForm form = (HtmlForm) page.getForms().get(0);
818 setFieldValue(page, "__login_user", USER_NETWORK_ID);
819 HtmlSubmitInput loginBtn = (HtmlSubmitInput) form.getInputByValue("Login");
820 page = (HtmlPage) loginBtn.click();
821 if (page.getTitleText().equals("Login")) {
822 page = (HtmlPage) loginBtn.click();
823 }
824 }
825 return page;
826 }
827
828 /**
829 * Gets an HTML element in the web page.
830 *
831 * Since some HTML elements don't use the id attribute, those HTML elements
832 * can found by searching for elements with the given values for the <b>name</b>,
833 * <b>value</b>, and <b>title</b> attributes. The first HTML element that
834 * matches is returned.
835 *
836 * HTML web pages may contain Inline Frames (iframes) which are not expanded
837 * within HtmlUnit. The inline frames contain inner web pages that must
838 * also be searched.
839 *
840 * @param page the HTML web page.
841 * @param name the value for the name attribute (may be null).
842 * @param value the value for the value attribute (may be null).
843 * @param title the value for the title attribute (may be null).
844 * @return the HTML element or null if not found.
845 */
846 protected final HtmlElement getElement(HtmlPage page, String name, String value, String title) {
847 HtmlElement element = getElement(page.getDocumentElement(), name, value, title);
848
849 if (element == null) {
850 List<HtmlPage> innerPages = getInnerPages(page);
851 for (HtmlPage innerPage : innerPages) {
852 element = getElement(innerPage, name, value, title);
853 if (element != null) break;
854 }
855 }
856
857 return element;
858 }
859
860 /**
861 * Gets an HTML element in an HTML element.
862 *
863 * Since some HTML elements don't use the id attribute, those HTML elements
864 * can found by searching for elements with the given values for the <b>name</b>,
865 * <b>value</b>, and <b>title</b> attributes. The first HTML element that
866 * matches is returned.
867 *
868 * @param element the HTML element.
869 * @param name the value for the name attribute (may be null).
870 * @param value the value for the value attribute (may be null).
871 * @param title the value for the title attribute (may be null).
872 * @return the HTML element or null if not found.
873 */
874 private HtmlElement getElement(HtmlElement element, String name, String value, String title) {
875 Iterator iterator = element.getAllHtmlChildElements();
876 while (iterator.hasNext()) {
877 HtmlElement child = (HtmlElement) iterator.next();
878 String nameValue = child.getAttributeValue("name");
879 String valueValue = child.getAttributeValue("value");
880 String titleValue = child.getAttributeValue("title");
881 if ((name == null || name.equals(nameValue)) &&
882 (value == null || value.equals(valueValue)) &&
883 (title == null || title.equals(titleValue))) {
884 return child;
885 }
886 }
887 return null;
888 }
889
890 /**
891 * Gets an HTML element in the web page.
892 *
893 * To find the HTML element, the following algorithm is used:
894 * <ol>
895 * <li>Search for a HTML element with an <b>id</b> attribute that matches the given id.</li>
896 * <li>If not found, search for the first HTML element with a <b>name</b> attribute that matches.</li>
897 * <li>If not found, search for the first HTML element with a <b>title</b> attribute that matches.</li>
898 * </ol>
899 *
900 * @param page the HTML web page to search.
901 * @param id the id of the HTML attribute.
902 * @return the HTML element or null if not found.
903 */
904 protected final HtmlElement getElement(HtmlPage page, String id) {
905 HtmlElement element = getElementById(page, id);
906 if (element == null) {
907 element = getElementByName(page, id);
908 if (element == null) {
909 element = getElementByTitle(page, id);
910 }
911 }
912 return element;
913 }
914
915 /**
916 * Gets an HTML element in the web page. Searches the web page for
917 * an HTML element whose id attribute matches the given id.
918 *
919 * HTML web pages may contain Inline Frames (iframes) which are not expanded
920 * within HtmlUnit. The inline frames contain inner web pages that must
921 * also be searched.
922 *
923 * @param page the HTML web page to search.
924 * @param id the id to search for.
925 * @return the HTML element or null if not found.
926 */
927 protected final HtmlElement getElementById(HtmlPage page, String id) {
928 HtmlElement element = null;
929 try {
930 element = page.getHtmlElementById(id);
931 } catch (ElementNotFoundException ex) {
932 List<HtmlPage> innerPages = getInnerPages(page);
933 for (HtmlPage innerPage : innerPages) {
934 element = getElementById(innerPage, id);
935 if (element != null) break;
936 }
937 }
938 return element;
939 }
940
941 /**
942 * Gets an HTML element in the web page. Searches the web page for
943 * the first HTML element whose name attribute matches the given name.
944 *
945 * HTML web pages may contain Inline Frames (iframes) which are not expanded
946 * within HtmlUnit. The inline frames contain inner web pages that must
947 * also be searched.
948 *
949 * @param page the HTML web page to search.
950 * @param name the name to search for.
951 * @return the HTML element or null if not found.
952 */
953 protected final HtmlElement getElementByName(HtmlPage page, String name) {
954 return getElementByName(page, name, false);
955 }
956
957 /**
958 * Gets an HTML element in a parent HTML element. Searches the parent HTML
959 * element for the first HTML element whose name attribute matches the given name.
960 *
961 * @param element the parent HTML element to search.
962 * @param name the name to search for.
963 * @return the HTML element or null if not found.
964 */
965 protected final HtmlElement getElementByName(HtmlElement element, String name) {
966 return getElementByName(element, name, false);
967 }
968
969 /**
970 * Gets an HTML element in the web page. Searches the web page for
971 * the first HTML element whose name attribute matches the given name.
972 *
973 * HTML web pages may contain Inline Frames (iframes) which are not expanded
974 * within HtmlUnit. The inline frames contain inner web pages that must
975 * also be searched.
976 *
977 * @param page the HTML web page to search.
978 * @param name the name to search for.
979 * @param startsWith if true, only match against the start of the name.
980 * @return the HTML element or null if not found.
981 */
982 protected final HtmlElement getElementByName(HtmlPage page, String name, boolean startsWith) {
983 HtmlElement element = getElementByName(page.getDocumentElement(), name, startsWith);
984
985 if (element == null) {
986 List<HtmlPage> innerPages = getInnerPages(page);
987 for (HtmlPage innerPage : innerPages) {
988 element = getElementByName(innerPage, name, startsWith);
989 if (element != null) break;
990 }
991 }
992
993 return element;
994 }
995
996 /**
997 * Gets an HTML element in a parent HTML element. Searches the parent HTML
998 * element for the first HTML element whose name attribute matches the given name.
999 *
1000 * @param element the parent HTML element to search.
1001 * @param name the name to search for.
1002 * @param startsWith if true, only match against the start of the name.
1003 * @return the HTML element or null if not found.
1004 */
1005 protected final HtmlElement getElementByName(HtmlElement element, String name, boolean startsWith) {
1006 Iterator iterator = element.getAllHtmlChildElements();
1007 while (iterator.hasNext()) {
1008 HtmlElement e = (HtmlElement) iterator.next();
1009 String value = e.getAttributeValue("name");
1010 if (!startsWith && name.equals(value)) {
1011 return e;
1012 } else if (startsWith && value != null && value.startsWith(name)) {
1013 return e;
1014 }
1015 }
1016 return null;
1017 }
1018
1019 /**
1020 * Gets an HTML element in the web page. Searches the web page for
1021 * the first HTML element whose title attribute matches the given title.
1022 *
1023 * HTML web pages may contain Inline Frames (iframes) which are not expanded
1024 * within HtmlUnit. The inline frames contain inner web pages that must
1025 * also be searched.
1026 *
1027 * @param page the HTML web page to search.
1028 * @param title the title to search for.
1029 * @return the HTML element or null if not found.
1030 */
1031 protected final HtmlElement getElementByTitle(HtmlPage page, String title) {
1032 HtmlElement element = getElementByTitle(page.getDocumentElement(), title);
1033
1034 if (element == null) {
1035 List<HtmlPage> innerPages = getInnerPages(page);
1036 for (HtmlPage innerPage : innerPages) {
1037 element = getElementByTitle(innerPage, title);
1038 if (element != null) break;
1039 }
1040 }
1041 return element;
1042 }
1043
1044 /**
1045 * Gets an HTML element in a parent HTML element. Searches the parent HTML
1046 * element for the first HTML element whose title attribute matches the given title.
1047 *
1048 * @param element the parent HTML element to search.
1049 * @param title the title to search for.
1050 * @return the HTML element or null if not found.
1051 */
1052 protected final HtmlElement getElementByTitle(HtmlElement element, String title) {
1053 Iterator iterator = element.getAllHtmlChildElements();
1054 while (iterator.hasNext()) {
1055 HtmlElement e = (HtmlElement) iterator.next();
1056 String value = e.getAttributeValue("title");
1057 if (title.equals(value)) {
1058 return e;
1059 }
1060 }
1061 return null;
1062 }
1063
1064 /**
1065 * Gets an HTML element in a parent HTML element. Searches the parent HTML
1066 * element for the first HTML element whose class attribute matches the given classname.
1067 *
1068 * @param element the parent HTML element to search.
1069 * @param classname the classname to search for.
1070 * @return the HTML element or null if not found.
1071 */
1072 protected final HtmlElement getElementByClass(HtmlElement element, String classname) {
1073 Iterator iterator = element.getAllHtmlChildElements();
1074 while (iterator.hasNext()) {
1075 HtmlElement e = (HtmlElement) iterator.next();
1076 String value = e.getAttributeValue("class");
1077 if (classname.equals(value)) {
1078 return e;
1079 }
1080 }
1081 return null;
1082 }
1083
1084 /**
1085 * Gets a Lookup HTML element. The searching for Lookup HTML elements
1086 * is a special case. This is because it lacks an id attribute and because
1087 * the value of its name attribute is extremely long and cryptic. To find
1088 * a Lookup element, the value of the name attribute is examined to see if
1089 * it contains the given id. Lookup HTML elements always contain some data
1090 * in the name attribute that is specific to the Lookup.
1091 *
1092 * HTML web pages may contain Inline Frames (iframes) which are not expanded
1093 * within HtmlUnit. The inline frames contain inner web pages that must
1094 * also be searched.
1095 *
1096 * @param page the HTML web page to search.
1097 * @param tag the tag to compare against Lookup HTML name attributes.
1098 * @return the Lookup's HTML element or null if not found.
1099 */
1100 protected final HtmlImageInput getLookup(HtmlPage page, String tag) {
1101 HtmlImageInput element = getLookup(page.getDocumentElement(), tag);
1102
1103 List<HtmlPage> innerPages = getInnerPages(page);
1104 for (Iterator<HtmlPage> page_it = innerPages.iterator(); page_it.hasNext() && element == null;) {
1105 HtmlPage innerPage = page_it.next();
1106 element = getLookup(innerPage, tag);
1107 }
1108
1109 return element;
1110 }
1111
1112 /**
1113 * Gets a Lookup HTML element. The searching for Lookup HTML elements
1114 * is a special case. This is because it lacks an id attribute and because
1115 * the value of its name attribute is extremely long and cryptic. To find
1116 * a Lookup element, the value of the name attribute is examined to see if
1117 * it contains the given id. Lookup HTML elements always contain some data
1118 * in the name attribute that is specific to the Lookup.
1119 *
1120 * @param element the parent HTML element to search.
1121 * @param tag the tag to compare against Lookup HTML name attributes.
1122 * @return the Lookup's HTML element or null if not found.
1123 */
1124 private final HtmlImageInput getLookup(HtmlElement element, String tag) {
1125 for (Iterator<HtmlElement> iterator = element.getAllHtmlChildElements(); iterator.hasNext();) {
1126 HtmlElement e = iterator.next();
1127 if (e instanceof HtmlImageInput) {
1128 String name = e.getAttributeValue("name");
1129 LOG.info("Found name attribute " + name);
1130 if (name != null && name.startsWith("methodToCall.performLookup") && name.contains(tag)) {
1131 return (HtmlImageInput) e;
1132 }
1133 }
1134 }
1135 return null;
1136 }
1137
1138 /**
1139 * Gets a HTML Table element. The search begins within the
1140 * first HTML element identified by <i>id</i>. If there are
1141 * multiple tables in the HTML element, only one is returned
1142 * and it is undefined which one it will be.
1143 *
1144 * @param page the HTML web page.
1145 * @param id identifies the HTML element to begin searching in.
1146 * @return the HTML table or null if not found.
1147 */
1148 protected final HtmlTable getTable(HtmlPage page, String id) {
1149 HtmlTable table = null;
1150 HtmlElement element = getElement(page, id);
1151 assertTrue(element != null);
1152
1153 if (element instanceof HtmlTable) {
1154 table = (HtmlTable) element;
1155 }
1156 else {
1157 Iterator iterator = element.getAllHtmlChildElements();
1158 while (iterator.hasNext()) {
1159 HtmlElement e = (HtmlElement) iterator.next();
1160 if (e instanceof HtmlTable) {
1161 table = (HtmlTable) e;
1162 break;
1163 }
1164 }
1165 }
1166 return table;
1167 }
1168
1169 /**
1170 * Gets an Anchor (hyperlink) element whose HREF attribute string contains
1171 * the given tag string.
1172 *
1173 * @param page the web page.
1174 * @param tag the string to look for in HREF.
1175 * @return the HTML Anchor element or null if not found.
1176 */
1177 protected final HtmlAnchor getAnchor(HtmlPage page, String tag) {
1178 HtmlAnchor element = getAnchor(page.getDocumentElement(), tag);
1179
1180 if (element == null) {
1181 List<HtmlPage> innerPages = getInnerPages(page);
1182 for (HtmlPage innerPage : innerPages) {
1183 element = getAnchor(innerPage, tag);
1184 if (element != null) break;
1185 }
1186 }
1187
1188 return element;
1189 }
1190
1191 /**
1192 * Gets an Anchor (hyperlink) element whose HREF attribute string contains
1193 * the given tag string.
1194 *
1195 * @param element the HTML element to look in.
1196 * @param tag the string to look for in HREF.
1197 * @return the HTML Anchor element or null if not found.
1198 */
1199 private HtmlAnchor getAnchor(HtmlElement element, String tag) {
1200 Iterator iterator = element.getAllHtmlChildElements();
1201 while (iterator.hasNext()) {
1202 HtmlElement e = (HtmlElement) iterator.next();
1203 if (e instanceof HtmlAnchor) {
1204 String href = e.getAttributeValue("href");
1205 if (href != null && href.contains(tag)) {
1206 return (HtmlAnchor) e;
1207 }
1208 }
1209 }
1210 return null;
1211 }
1212
1213 /**
1214 * Finds all of the Help HTML elements (hyperlinks) in a web page.
1215 *
1216 * HTML web pages may contain Inline Frames (iframes) which are not expanded
1217 * within HtmlUnit. The inline frames contain inner web pages that must
1218 * also be searched.
1219 *
1220 * @param page the HTML web page.
1221 * @return the list of Help HTML elements.
1222 */
1223 protected final List<HtmlAnchor> findHelpLinks(HtmlPage page) {
1224 List <HtmlAnchor> helpLinks = findHelpLinks(page.getDocumentElement());
1225
1226 List<HtmlPage> innerPages = getInnerPages(page);
1227 for (HtmlPage innerPage : innerPages) {
1228 helpLinks.addAll(findHelpLinks(innerPage));
1229 }
1230
1231 return helpLinks;
1232 }
1233
1234 /**
1235 * Finds all of the Help HTML elements (hyperlinks) in a parent HTML element.
1236 * In Kuali, all Help hyperlinks have a target window with value of "helpWindow".
1237 *
1238 * @param element the parent HTML element.
1239 * @return the list of Help HTML elements.
1240 */
1241 private List<HtmlAnchor> findHelpLinks(HtmlElement element) {
1242 List<HtmlAnchor> helpLinks = new ArrayList<HtmlAnchor>();
1243 Iterator iterator = element.getAllHtmlChildElements();
1244 while (iterator.hasNext()) {
1245 HtmlElement e = (HtmlElement) iterator.next();
1246 if (e instanceof HtmlAnchor) {
1247 String target = e.getAttributeValue("target");
1248 if (target != null && target.equals("helpWindow")) {
1249 helpLinks.add((HtmlAnchor) e);
1250 }
1251 }
1252 }
1253 return helpLinks;
1254 }
1255
1256 /**
1257 * Gets an HTML element in the web page. Searches the web page for
1258 * the first HTML element whose name attribute matches the given name.
1259 *
1260 * HTML web pages may contain Inline Frames (iframes) which are not expanded
1261 * within HtmlUnit. The inline frames contain inner web pages that must
1262 * also be searched.
1263 *
1264 * @param page the HTML web page to search.
1265 * @param name the name to search for.
1266 * @param startsWith if true, only match against the start of the name.
1267 * @return the HTML element or null if not found.
1268 */
1269 protected final List<HtmlElement> getAllElementsByName(HtmlPage page, String name, boolean startsWith) {
1270 List<HtmlElement> elements = getAllElementsByName(page.getDocumentElement(), name, startsWith);
1271
1272 List<HtmlPage> innerPages = getInnerPages(page);
1273 for (HtmlPage innerPage : innerPages) {
1274 elements.addAll(getAllElementsByName(innerPage, name, startsWith));
1275 }
1276
1277 return elements;
1278 }
1279
1280 /**
1281 * Gets an HTML element in a parent HTML element. Searches the parent HTML
1282 * element for the first HTML element whose name attribute matches the given name.
1283 *
1284 * @param element the parent HTML element to search.
1285 * @param name the name to search for.
1286 * @param startsWith if true, only match against the start of the name.
1287 * @return the HTML element or null if not found.
1288 */
1289 protected final List<HtmlElement> getAllElementsByName(HtmlElement element, String name, boolean startsWith) {
1290 List<HtmlElement> elements = new ArrayList<HtmlElement>();
1291 Iterator iterator = element.getAllHtmlChildElements();
1292 while (iterator.hasNext()) {
1293 HtmlElement e = (HtmlElement) iterator.next();
1294 String value = e.getAttributeValue("name");
1295 if (!startsWith && name.equals(value)) {
1296 elements.add(e);
1297 } else if (startsWith && value != null && value.startsWith(name)) {
1298 elements.add(e);
1299 }
1300 }
1301 return elements;
1302 }
1303
1304 /**
1305 * Gets the list of inner web pages. Inner web pages are contained
1306 * within Inline Frames (iframes).
1307 *
1308 * @param page the HTML web page to search for inner web pages.
1309 * @return the list of inner HTML web pages.
1310 */
1311 protected final List<HtmlPage> getInnerPages(HtmlPage page) {
1312 List<HtmlPage> innerPages = new ArrayList<HtmlPage>();
1313
1314 List frames = page.getFrames();
1315 for (int i = 0; i < frames.size(); i++) {
1316 FrameWindow frame = (FrameWindow) frames.get(i);
1317 BaseFrame baseFrame = frame.getFrameElement();
1318 if (baseFrame instanceof HtmlInlineFrame) {
1319 HtmlInlineFrame iframe = (HtmlInlineFrame) baseFrame;
1320 Page epage = iframe.getEnclosedPage();
1321 if (epage instanceof HtmlPage) {
1322 innerPages.add((HtmlPage) epage);
1323 }
1324 }
1325 }
1326 return innerPages;
1327 }
1328
1329 /**
1330 * Get the first HTML child element.
1331 *
1332 * @param element the parent HTML element.
1333 * @return the first child HTML element or null if there are no children.
1334 */
1335 protected final HtmlElement getFirstChild(HtmlElement element) {
1336 HtmlElement firstChild = null;
1337 Iterator iterator = element.getChildElementsIterator();
1338 if (iterator.hasNext()) {
1339 firstChild = (HtmlElement) iterator.next();
1340 }
1341 return firstChild;
1342 }
1343
1344 /**
1345 * Get the next sibling HTML element.
1346 *
1347 * @param element an HTML element.
1348 * @return the next sibling HTML element or null if there is none.
1349 */
1350 protected final HtmlElement getNextSibling(HtmlElement element) {
1351 HtmlElement sibling = null;
1352 DomNode node = element.getParentNode();
1353 if (node instanceof HtmlElement) {
1354 HtmlElement parent = (HtmlElement) node;
1355 Iterator iterator = parent.getChildElementsIterator();
1356 while (iterator.hasNext()) {
1357 HtmlElement e = (HtmlElement) iterator.next();
1358 if (e == element) {
1359 if (iterator.hasNext()) {
1360 sibling = (HtmlElement) iterator.next();
1361 }
1362 break;
1363 }
1364 }
1365 }
1366 return sibling;
1367 }
1368
1369 /**
1370 * Do a document search looking for the a specific document based upon its docNbr.
1371 * The search begins at the Portal Page and then navigates to the Doc Search Page.
1372 * The given docNbr is added to the search field and search is performed. If the
1373 * doc is found, it's hyperlink is clicked and the resulting page is returned.
1374 *
1375 * @param docNbr the docNbr to search for
1376 * @return the document's page or null if not found.
1377 * @throws IOException
1378 */
1379 protected final HtmlPage docSearch(String docNbr) throws IOException {
1380 HtmlPage docPage = null;
1381 HtmlPage portalPage = getPortalPage();
1382 HtmlPage docSearchPage = clickOn(portalPage, "Document Search");
1383
1384 setFieldValue(docSearchPage, "criteria.routeHeaderId", docNbr);
1385 docSearchPage = clickOn(docSearchPage, "methodToCall.doDocSearch");
1386 if (docSearchPage.asText().contains("Nothing found to display.")) {
1387 docPage = null;
1388 } else {
1389 HtmlAnchor hyperlink = getAnchor(docSearchPage, docNbr);
1390 assertNotNull(hyperlink);
1391 docPage = clickOn(hyperlink);
1392 }
1393 return docPage;
1394 }
1395
1396 /**
1397 * Gets the docNbr from a document's web page.
1398 * The docNbr is expected to be in an HTML table
1399 * is the division labeled "headerarea".
1400 *
1401 * @param page the documen's web page
1402 * @return the document's docNbr
1403 */
1404 protected final String getDocNbr(HtmlPage page) {
1405 HtmlTable table = getTable(page, "headerarea");
1406 HtmlTableRow row = table.getRow(0);
1407 HtmlTableCell cell = row.getCell(1);
1408 return cell.asText().trim();
1409 }
1410
1411 /**
1412 * Save a document, i.e. click on Save button.
1413 *
1414 * @param page the document web page
1415 * @return the next page
1416 * @throws IOException
1417 */
1418 protected final HtmlPage saveDoc(HtmlPage page) throws IOException {
1419 return clickOn(page, "save");
1420 }
1421
1422 /**
1423 * Closes a document. If queried to save the document, the
1424 * "No" button is clicked on.
1425 *
1426 * @param page the document web page
1427 * @return the next page
1428 * @throws IOException
1429 */
1430 protected final HtmlPage closeDoc(HtmlPage page) throws IOException {
1431 HtmlPage nextPage = clickOn(page, "close");
1432 if (nextPage.asText().contains("Would you like to save this document before you close it")) {
1433 nextPage = clickOn(nextPage, "methodToCall.processAnswer.button1");
1434 }
1435 return nextPage;
1436 }
1437
1438 /**
1439 * Saves and closes a document. After saving and closing the document,
1440 * the document's number is returned. It would have been logical to
1441 * return the next web page, but in this testing environment, it is
1442 * anticipated that the document number will be used to perform a document
1443 * search.
1444 *
1445 * @param page the document web page
1446 * @return the document's number
1447 * @throws IOException
1448 */
1449 protected final String saveAndCloseDoc(HtmlPage page) throws IOException {
1450 String docNbr = getDocNbr(page);
1451 HtmlPage savedPage = saveDoc(page);
1452 closeDoc(savedPage);
1453 return docNbr;
1454 }
1455
1456 /**
1457 * Saves a document and then performs a document search to retrieve
1458 * the document. This is useful for verifying that a document can be
1459 * saved correctly. After retrieving a saved document, its values can
1460 * be inspected to verify their correctness.
1461 *
1462 * @param docPage the web page containing the document.
1463 * @return the retrieved document web page.
1464 * @throws Exception
1465 */
1466 protected final HtmlPage saveAndSearchDoc(HtmlPage docPage) throws Exception {
1467 String docNbr = saveAndCloseDoc(docPage);
1468 docPage = docSearch(docNbr);
1469 assertNotNull(docPage);
1470 return docPage;
1471 }
1472
1473 /**
1474 * Gets the list of options in a select field. This list only
1475 * contains the text that is displayed to the user, not the
1476 * actual values sent to the web server in a POST.
1477 *
1478 * @param page the web page.
1479 * @param id the id of the select field.
1480 * @return the list of displayed options.
1481 */
1482 protected final List<String> getSelectOptions(HtmlPage page, String id) {
1483 List<String> options = new ArrayList<String>();
1484
1485 HtmlElement element = getElement(page, id);
1486 assertNotNull(element);
1487 assertTrue(element instanceof HtmlSelect);
1488
1489 HtmlSelect select = (HtmlSelect) element;
1490 for (int i = 0; i < select.getOptionSize(); i++) {
1491 HtmlOption option = select.getOption(i);
1492 options.add(option.asText().trim());
1493 }
1494
1495 return options;
1496 }
1497
1498 /**
1499 * Verify that all the Help links on the web page go to the Kuali Help Web Page.
1500 * This will also test the help links on other panels on the page, but no big deal. This is not enabled in
1501 * tests by default. If you want your test to do this, you need to add the <code>@Test</code> annotation
1502 * and override it in your test class.
1503 * @throws Exception
1504 */
1505 public void testHelpLinks() throws Exception {
1506 checkHelpLinks(getDefaultDocumentPage());
1507 }
1508
1509 /**
1510 * Used by generic methods like <code>{@link #testHelpLinks()}</code> for general behavior that is particular to a
1511 * page, but we don't know what that page is. This should be <code>abstract</code>, but that would break stuff.
1512 *
1513 * @return <code>{@link HtmlPage}</code> instance of a page that is used frequently in this test class
1514 */
1515 protected HtmlPage getDefaultDocumentPage() throws Exception {
1516 return null;
1517 }
1518
1519 /**
1520 * Sets a select field to any of options except for "select:".
1521 * This is useful if you don't know the possible options in a
1522 * select field and you don't care which one is selected.
1523 *
1524 * @param page the HTML web page.
1525 * @param id the id of the HTML select tag.
1526 */
1527 protected void selectAnyOption(HtmlPage page, String id) {
1528 HtmlElement element = getElement(page, id);
1529 assertTrue(element instanceof HtmlSelect);
1530
1531 HtmlSelect selectField = (HtmlSelect) element;
1532 List<HtmlOption> options = selectField.getOptions();
1533 for (HtmlOption option : options) {
1534 String value = option.getValueAttribute();
1535 if (!value.equals("")) {
1536 selectField.setSelectedAttribute(value, true);
1537 break;
1538 }
1539 }
1540 }
1541 }