001: /*
002: * Copyright (c) 2002-2008 Gargoyle Software Inc. All rights reserved.
003: *
004: * Redistribution and use in source and binary forms, with or without
005: * modification, are permitted provided that the following conditions are met:
006: *
007: * 1. Redistributions of source code must retain the above copyright notice,
008: * this list of conditions and the following disclaimer.
009: * 2. Redistributions in binary form must reproduce the above copyright notice,
010: * this list of conditions and the following disclaimer in the documentation
011: * and/or other materials provided with the distribution.
012: * 3. The end-user documentation included with the redistribution, if any, must
013: * include the following acknowledgment:
014: *
015: * "This product includes software developed by Gargoyle Software Inc.
016: * (http://www.GargoyleSoftware.com/)."
017: *
018: * Alternately, this acknowledgment may appear in the software itself, if
019: * and wherever such third-party acknowledgments normally appear.
020: * 4. The name "Gargoyle Software" must not be used to endorse or promote
021: * products derived from this software without prior written permission.
022: * For written permission, please contact info@GargoyleSoftware.com.
023: * 5. Products derived from this software may not be called "HtmlUnit", nor may
024: * "HtmlUnit" appear in their name, without prior written permission of
025: * Gargoyle Software Inc.
026: *
027: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
028: * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
029: * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GARGOYLE
030: * SOFTWARE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
031: * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
032: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
033: * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
034: * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
035: * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
036: * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
037: */
038: package com.gargoylesoftware.htmlunit;
039:
040: import java.util.ArrayList;
041: import java.util.Arrays;
042: import java.util.Iterator;
043: import java.util.List;
044:
045: import org.jaxen.JaxenException;
046:
047: import com.gargoylesoftware.htmlunit.html.HtmlAnchor;
048: import com.gargoylesoftware.htmlunit.html.HtmlElement;
049: import com.gargoylesoftware.htmlunit.html.HtmlInput;
050: import com.gargoylesoftware.htmlunit.html.HtmlPage;
051:
052: /**
053: * Utility class which contains standard assertions for HTML pages.
054: *
055: * @version $Revision: 2132 $
056: * @author Daniel Gredler
057: */
058: public final class WebAssert {
059:
060: /**
061: * Private to prevent instantiation.
062: */
063: private WebAssert() {
064: // Empty.
065: }
066:
067: /**
068: * Verifies that the specified page's title equals the specified expected title.
069: *
070: * @param page the page to check
071: * @param title the expected title
072: */
073: public static void assertTitleEquals(final HtmlPage page,
074: final String title) {
075: final String s = page.getTitleText();
076: if (!s.equals(title)) {
077: final String msg = "Actual page title '" + s
078: + "' does not match expected page title '" + title
079: + "'.";
080: throw new AssertionError(msg);
081: }
082: }
083:
084: /**
085: * Verifies that the specified page's title contains the specified substring.
086: *
087: * @param page the page to check
088: * @param titlePortion the substring which the page title is expected to contain
089: */
090: public static void assertTitleContains(final HtmlPage page,
091: final String titlePortion) {
092: final String s = page.getTitleText();
093: if (s.indexOf(titlePortion) == -1) {
094: final String msg = "Page title '" + s
095: + "' does not contain the substring '"
096: + titlePortion + "'.";
097: throw new AssertionError(msg);
098: }
099: }
100:
101: /**
102: * Verifies that the specified page's title matches the specified regular expression.
103: *
104: * @param page the page to check
105: * @param regex the regular expression that the page title is expected to match
106: */
107: public static void assertTitleMatches(final HtmlPage page,
108: final String regex) {
109: final String s = page.getTitleText();
110: if (!s.matches(regex)) {
111: final String msg = "Page title '" + s
112: + "' does not match the regular expression '"
113: + regex + "'.";
114: throw new AssertionError(msg);
115: }
116: }
117:
118: /**
119: * Verifies that the specified page contains an element with the specified ID.
120: *
121: * @param page the page to check
122: * @param id the expected ID of an element in the page
123: */
124: public static void assertElementPresent(final HtmlPage page,
125: final String id) {
126: try {
127: page.getHtmlElementById(id);
128: } catch (final ElementNotFoundException e) {
129: final String msg = "The page does not contain an element with ID '"
130: + id + "'.";
131: throw new AssertionError(msg);
132: }
133: }
134:
135: /**
136: * Verifies that the specified page contains an element matching the specified XPath expression.
137: *
138: * @param page the page to check
139: * @param xpath the XPath expression which is expected to match an element in the page
140: */
141: public static void assertElementPresentByXPath(final HtmlPage page,
142: final String xpath) {
143: try {
144: final List elements = page.getByXPath(xpath);
145: if (elements.isEmpty()) {
146: final String msg = "The page does not contain any elements matching the XPath expression '"
147: + xpath + "'.";
148: throw new AssertionError(msg);
149: }
150: } catch (final JaxenException e) {
151: final String msg = "Unable to process XPath expression '"
152: + xpath + "'.";
153: throw new AssertionError(msg);
154: }
155: }
156:
157: /**
158: * Verifies that the specified page does not contain an element with the specified ID.
159: *
160: * @param page the page to check
161: * @param id the ID of an element which expected to not exist on the page
162: */
163: public static void assertElementNotPresent(final HtmlPage page,
164: final String id) {
165: try {
166: page.getHtmlElementById(id);
167: } catch (final ElementNotFoundException e) {
168: return;
169: }
170: final String msg = "The page contains an element with ID '"
171: + id + "'.";
172: throw new AssertionError(msg);
173: }
174:
175: /**
176: * Verifies that the specified page does not contain an element matching the specified XPath
177: * expression.
178: *
179: * @param page the page to check
180: * @param xpath the XPath expression which is expected to not match an element in the page
181: */
182: public static void assertElementNotPresentByXPath(
183: final HtmlPage page, final String xpath) {
184: try {
185: final List elements = page.getByXPath(xpath);
186: if (!elements.isEmpty()) {
187: final String msg = "The page does not contain any elements matching the XPath expression '"
188: + xpath + "'.";
189: throw new AssertionError(msg);
190: }
191: } catch (final JaxenException e) {
192: final String msg = "Unable to process XPath expression '"
193: + xpath + "'.";
194: throw new AssertionError(msg);
195: }
196: }
197:
198: /**
199: * Verifies that the specified page contains the specified text.
200: *
201: * @param page the page to check
202: * @param text the text to check for
203: */
204: public static void assertTextPresent(final HtmlPage page,
205: final String text) {
206: if (page.asText().indexOf(text) == -1) {
207: final String msg = "The page does not contain the text '"
208: + text + "'.";
209: throw new AssertionError(msg);
210: }
211: }
212:
213: /**
214: * Verifies that the element on the specified page which matches the specified ID contains the
215: * specified text.
216: *
217: * @param page the page to check
218: * @param text the text to check for
219: * @param id the ID of the element which is expected to contain the specified text
220: */
221: public static void assertTextPresentInElement(final HtmlPage page,
222: final String text, final String id) {
223: try {
224: final HtmlElement element = page.getHtmlElementById(id);
225: if (element.asText().indexOf(text) == -1) {
226: final String msg = "The element with ID '" + id
227: + "' does not contain the text '" + text + "'.";
228: throw new AssertionError(msg);
229: }
230: } catch (final ElementNotFoundException e) {
231: final String msg = "Unable to verify that the element with ID '"
232: + id
233: + "' contains the text '"
234: + text
235: + "' because the specified element does not exist.";
236: throw new AssertionError(msg);
237: }
238: }
239:
240: /**
241: * Verifies that the specified page does not contain the specified text.
242: *
243: * @param page the page to check
244: * @param text the text to check for
245: */
246: public static void assertTextNotPresent(final HtmlPage page,
247: final String text) {
248: if (page.asText().indexOf(text) != -1) {
249: final String msg = "The page contains the text '" + text
250: + "'.";
251: throw new AssertionError(msg);
252: }
253: }
254:
255: /**
256: * Verifies that the element on the specified page which matches the specified ID does not
257: * contain the specified text.
258: *
259: * @param page the page to check
260: * @param text the text to check for
261: * @param id the ID of the element which is expected to not contain the specified text
262: */
263: public static void assertTextNotPresentInElement(
264: final HtmlPage page, final String text, final String id) {
265: try {
266: final HtmlElement element = page.getHtmlElementById(id);
267: if (element.asText().indexOf(text) != -1) {
268: final String msg = "The element with ID '" + id
269: + "' contains the text '" + text + "'.";
270: throw new AssertionError(msg);
271: }
272: } catch (final ElementNotFoundException e) {
273: final String msg = "Unable to verify that the element with ID '"
274: + id
275: + "' does not contain the text '"
276: + text
277: + "' because the specified element does not exist.";
278: throw new AssertionError(msg);
279: }
280: }
281:
282: /**
283: * Verifies that the specified page contains a link with the specified ID.
284: *
285: * @param page the page to check
286: * @param id the ID of the link which the page is expected to contain
287: */
288: public static void assertLinkPresent(final HtmlPage page,
289: final String id) {
290: try {
291: page.getDocumentHtmlElement().getOneHtmlElementByAttribute(
292: "a", "id", id);
293: } catch (final ElementNotFoundException e) {
294: final String msg = "The page does not contain a link with ID '"
295: + id + "'.";
296: throw new AssertionError(msg);
297: }
298: }
299:
300: /**
301: * Verifies that the specified page does not contain a link with the specified ID.
302: *
303: * @param page the page to check
304: * @param id the ID of the link which the page is expected to not contain
305: */
306: public static void assertLinkNotPresent(final HtmlPage page,
307: final String id) {
308: try {
309: page.getDocumentHtmlElement().getOneHtmlElementByAttribute(
310: "a", "id", id);
311: // Not expected.
312: final String msg = "The page contains a link with ID '"
313: + id + "'.";
314: throw new AssertionError(msg);
315: } catch (final ElementNotFoundException e) {
316: // Expected.
317: }
318: }
319:
320: /**
321: * Verifies that the specified page contains a link with the specified text. The specified text
322: * may be a substring of the entire text contained by the link.
323: *
324: * @param page the page to check
325: * @param text the text which a link in the specified page is expected to contain
326: */
327: public static void assertLinkPresentWithText(final HtmlPage page,
328: final String text) {
329: boolean found = false;
330: for (final Iterator i = page.getAnchors().iterator(); i
331: .hasNext();) {
332: final HtmlAnchor a = (HtmlAnchor) i.next();
333: if (a.asText().indexOf(text) != -1) {
334: found = true;
335: break;
336: }
337: }
338: if (!found) {
339: final String msg = "The page does not contain a link with text '"
340: + text + "'.";
341: throw new AssertionError(msg);
342: }
343: }
344:
345: /**
346: * Verifies that the specified page does not contain a link with the specified text. The
347: * specified text may be a substring of the entire text contained by the link.
348: *
349: * @param page the page to check
350: * @param text the text which a link in the specified page is not expected to contain
351: */
352: public static void assertLinkNotPresentWithText(
353: final HtmlPage page, final String text) {
354: boolean found = false;
355: for (final Iterator i = page.getAnchors().iterator(); i
356: .hasNext();) {
357: final HtmlAnchor a = (HtmlAnchor) i.next();
358: if (a.asText().indexOf(text) != -1) {
359: found = true;
360: break;
361: }
362: }
363: if (found) {
364: final String msg = "The page contains a link with text '"
365: + text + "'.";
366: throw new AssertionError(msg);
367: }
368: }
369:
370: /**
371: * Verifies that the specified page contains a form with the specified name.
372: *
373: * @param page the page to check
374: * @param name the expected name of a form on the page
375: */
376: public static void assertFormPresent(final HtmlPage page,
377: final String name) {
378: try {
379: page.getFormByName(name);
380: } catch (final ElementNotFoundException e) {
381: final String msg = "The page does not contain a form named '"
382: + name + "'.";
383: throw new AssertionError(msg);
384: }
385: }
386:
387: /**
388: * Verifies that the specified page does not contain a form with the specified name.
389: *
390: * @param page the page to check
391: * @param name the name of a form which should not exist on the page
392: */
393: public static void assertFormNotPresent(final HtmlPage page,
394: final String name) {
395: try {
396: page.getFormByName(name);
397: } catch (final ElementNotFoundException e) {
398: return;
399: }
400: final String msg = "The page contains a form named '" + name
401: + "'.";
402: throw new AssertionError(msg);
403: }
404:
405: /**
406: * Verifies that the specified page contains an input element with the specified name.
407: *
408: * @param page the page to check
409: * @param name the name of the input element to look for
410: */
411: public static void assertInputPresent(final HtmlPage page,
412: final String name) {
413: final String xpath = "//input[@name='" + name + "']";
414: try {
415: final List list = page.getByXPath(xpath);
416: if (list.isEmpty()) {
417: throw new AssertionError(
418: "Unable to find an input element named '"
419: + name + "'.");
420: }
421: } catch (final JaxenException e) {
422: throw new AssertionError(
423: "Unable to process XPath expression '" + xpath
424: + "'.");
425: }
426: }
427:
428: /**
429: * Verifies that the specified page does not contain an input element with the specified name.
430: *
431: * @param page the page to check
432: * @param name the name of the input element to look for
433: */
434: public static void assertInputNotPresent(final HtmlPage page,
435: final String name) {
436: final String xpath = "//input[@name='" + name + "']";
437: try {
438: final List list = page.getByXPath(xpath);
439: if (!list.isEmpty()) {
440: throw new AssertionError(
441: "Unable to find an input element named '"
442: + name + "'.");
443: }
444: } catch (final JaxenException e) {
445: throw new AssertionError(
446: "Unable to process XPath expression '" + xpath
447: + "'.");
448: }
449: }
450:
451: /**
452: * Verifies that the input element with the specified name on the specified page contains the
453: * specified value.
454: *
455: * @param page the page to check
456: * @param name the name of the input element to check
457: * @param value the value to check for
458: */
459: public static void assertInputContainsValue(final HtmlPage page,
460: final String name, final String value) {
461: final String xpath = "//input[@name='" + name + "']";
462: try {
463: final List list = page.getByXPath(xpath);
464: if (list.isEmpty()) {
465: throw new AssertionError(
466: "Unable to find an input element named '"
467: + name + "'.");
468: }
469: final HtmlInput input = (HtmlInput) list.get(0);
470: final String s = input.getValueAttribute();
471: if (!s.equals(value)) {
472: throw new AssertionError("The input element named '"
473: + name + "' contains the value '" + s
474: + "', not the expected value '" + value + "'.");
475: }
476: } catch (final JaxenException e) {
477: throw new AssertionError(
478: "Unable to process XPath expression '" + xpath
479: + "'.");
480: }
481: }
482:
483: /**
484: * Verifies that the input element with the specified name on the specified page does not
485: * contain the specified value.
486: *
487: * @param page the page to check
488: * @param name the name of the input element to check
489: * @param value the value to check for
490: */
491: public static void assertInputDoesNotContainValue(
492: final HtmlPage page, final String name, final String value) {
493: final String xpath = "//input[@name='" + name + "']";
494: try {
495: final List list = page.getByXPath(xpath);
496: if (list.isEmpty()) {
497: throw new AssertionError(
498: "Unable to find an input element named '"
499: + name + "'.");
500: }
501: final HtmlInput input = (HtmlInput) list.get(0);
502: final String s = input.getValueAttribute();
503: if (s.equals(value)) {
504: throw new AssertionError("The input element named '"
505: + name + "' contains the value '" + s
506: + "', not the expected value '" + value + "'.");
507: }
508: } catch (final JaxenException e) {
509: throw new AssertionError(
510: "Unable to process XPath expression '" + xpath
511: + "'.");
512: }
513: }
514:
515: /**
516: * <p>Many HTML elements are "tabbable" and can have a <tt>tabindex</tt> attribute
517: * that determines the order in which the components are navigated when
518: * pressing the tab key. To ensure good usability for keyboard navigation,
519: * all tabbable elements should have the <tt>tabindex</tt> attribute set.</p>
520: *
521: * <p>This method verifies that all tabbable elements have a valid value set for
522: * the <tt>tabindex</tt> attribute.</p>
523: *
524: * @param page the page to check
525: */
526: public static void assertAllTabIndexAttributesSet(
527: final HtmlPage page) {
528: final List tags = Arrays.asList(new Object[] { "a", "area",
529: "button", "input", "object", "select", "textarea" });
530: final List tabbableElements = page.getDocumentHtmlElement()
531: .getHtmlElementsByTagNames(tags);
532: for (final Iterator i = tabbableElements.iterator(); i
533: .hasNext();) {
534: final HtmlElement element = (HtmlElement) i.next();
535: final Short tabIndex = element.getTabIndex();
536: if (tabIndex == null
537: || tabIndex == HtmlElement.TAB_INDEX_OUT_OF_BOUNDS) {
538: final String s = element.getAttributeValue("tabindex");
539: throw new AssertionError(
540: "Illegal value for tab index: '" + s + "'.");
541: }
542: }
543: }
544:
545: /**
546: * Many HTML components can have an <tt>accesskey</tt> attribute which defines a hot key for
547: * keyboard navigation. This method verifies that all the <tt>accesskey</tt> attributes on the
548: * specified page are unique.
549: *
550: * @param page the page to check
551: */
552: public static void assertAllAccessKeyAttributesUnique(
553: final HtmlPage page) {
554: final List list = new ArrayList();
555: for (final Iterator i = page.getAllHtmlChildElements(); i
556: .hasNext();) {
557: final HtmlElement element = (HtmlElement) i.next();
558: final String key = element.getAttributeValue("accesskey");
559: if (key != null && key.length() != 0) {
560: if (list.contains(key)) {
561: throw new AssertionError("The access key '" + key
562: + "' is not unique.");
563: } else {
564: list.add(key);
565: }
566: }
567: }
568: }
569:
570: /**
571: * Verifies that all element IDs in the specified page are unique.
572: *
573: * @param page the page to check
574: */
575: public static void assertAllIdAttributesUnique(final HtmlPage page) {
576: final List list = new ArrayList();
577: for (final Iterator i = page.getAllHtmlChildElements(); i
578: .hasNext();) {
579: final HtmlElement element = (HtmlElement) i.next();
580: final String id = element.getAttributeValue("id");
581: if (id != null && id.length() != 0) {
582: if (list.contains(id)) {
583: throw new AssertionError("The element ID '" + id
584: + "' is not unique.");
585: } else {
586: list.add(id);
587: }
588: }
589: }
590: }
591:
592: }
|