View Javadoc
1   /*
2    * Copyright (c) 2002-2025 Gargoyle Software Inc.
3    *
4    * Licensed under the Apache 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    * https://www.apache.org/licenses/LICENSE-2.0
8    *
9    * Unless required by applicable law or agreed to in writing, software
10   * distributed under the License is distributed on an "AS IS" BASIS,
11   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12   * See the License for the specific language governing permissions and
13   * limitations under the License.
14   */
15  package org.htmlunit;
16  
17  import java.util.ArrayList;
18  import java.util.Arrays;
19  import java.util.List;
20  
21  import org.htmlunit.html.DomElement;
22  import org.htmlunit.html.HtmlAnchor;
23  import org.htmlunit.html.HtmlElement;
24  import org.htmlunit.html.HtmlInput;
25  import org.htmlunit.html.HtmlPage;
26  
27  /**
28   * Utility class which contains standard assertions for HTML pages.
29   *
30   * @author Daniel Gredler
31   * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
32   * @author Ahmed Ashour
33   */
34  public final class WebAssert {
35  
36      /**
37       * Private to prevent instantiation.
38       */
39      private WebAssert() {
40          // Empty.
41      }
42  
43      /**
44       * Verifies that the specified page's title equals the specified expected title.
45       *
46       * @param page the page to check
47       * @param title the expected title
48       */
49      public static void assertTitleEquals(final HtmlPage page, final String title) {
50          final String s = page.getTitleText();
51          if (!s.equals(title)) {
52              final String msg = "Actual page title '" + s + "' does not match expected page title '" + title + "'.";
53              throw new AssertionError(msg);
54          }
55      }
56  
57      /**
58       * Verifies that the specified page's title contains the specified substring.
59       *
60       * @param page the page to check
61       * @param titlePortion the substring which the page title is expected to contain
62       */
63      public static void assertTitleContains(final HtmlPage page, final String titlePortion) {
64          final String s = page.getTitleText();
65          if (!s.contains(titlePortion)) {
66              final String msg = "Page title '" + s + "' does not contain the substring '" + titlePortion + "'.";
67              throw new AssertionError(msg);
68          }
69      }
70  
71      /**
72       * Verifies that the specified page's title matches the specified regular expression.
73       *
74       * @param page the page to check
75       * @param regex the regular expression that the page title is expected to match
76       */
77      public static void assertTitleMatches(final HtmlPage page, final String regex) {
78          final String s = page.getTitleText();
79          if (!s.matches(regex)) {
80              final String msg = "Page title '" + s + "' does not match the regular expression '" + regex + "'.";
81              throw new AssertionError(msg);
82          }
83      }
84  
85      /**
86       * Verifies that the specified page contains an element with the specified ID.
87       *
88       * @param page the page to check
89       * @param id the expected ID of an element in the page
90       */
91      public static void assertElementPresent(final HtmlPage page, final String id) {
92          try {
93              page.getHtmlElementById(id);
94          }
95          catch (final ElementNotFoundException e) {
96              final String msg = "The page does not contain an element with ID '" + id + "'.";
97              throw new AssertionError(msg, e);
98          }
99      }
100 
101     /**
102      * Verifies that the specified page contains an element matching the specified XPath expression.
103      *
104      * @param page the page to check
105      * @param xpath the XPath expression which is expected to match an element in the page
106      */
107     public static void assertElementPresentByXPath(final HtmlPage page, final String xpath) {
108         final List<?> elements = page.getByXPath(xpath);
109         if (elements.isEmpty()) {
110             final String msg = "The page does not contain any elements matching the XPath expression '" + xpath
111                             + "'.";
112             throw new AssertionError(msg);
113         }
114     }
115 
116     /**
117      * Verifies that the specified page does not contain an element with the specified ID.
118      *
119      * @param page the page to check
120      * @param id the ID of an element which expected to not exist on the page
121      */
122     public static void assertElementNotPresent(final HtmlPage page, final String id) {
123         try {
124             page.getHtmlElementById(id);
125         }
126         catch (final ElementNotFoundException e) {
127             return;
128         }
129         final String msg = "The page contains an element with ID '" + id + "'.";
130         throw new AssertionError(msg);
131     }
132 
133     /**
134      * Verifies that the specified page does not contain an element matching the specified XPath
135      * expression.
136      *
137      * @param page the page to check
138      * @param xpath the XPath expression which is expected to not match an element in the page
139      */
140     public static void assertElementNotPresentByXPath(final HtmlPage page, final String xpath) {
141         final List<?> elements = page.getByXPath(xpath);
142         if (!elements.isEmpty()) {
143             final String msg = "The page does not contain any elements matching the XPath expression '" + xpath
144                             + "'.";
145             throw new AssertionError(msg);
146         }
147     }
148 
149     /**
150      * Verifies that the specified page contains the specified text.
151      *
152      * @param page the page to check
153      * @param text the text to check for
154      */
155     public static void assertTextPresent(final HtmlPage page, final String text) {
156         if (!page.asNormalizedText().contains(text)) {
157             final String msg = "The page does not contain the text '" + text + "'.";
158             throw new AssertionError(msg);
159         }
160     }
161 
162     /**
163      * Verifies that the element on the specified page which matches the specified ID contains the
164      * specified text.
165      *
166      * @param page the page to check
167      * @param text the text to check for
168      * @param id the ID of the element which is expected to contain the specified text
169      */
170     public static void assertTextPresentInElement(final HtmlPage page, final String text, final String id) {
171         try {
172             final HtmlElement element = page.getHtmlElementById(id);
173             if (!element.asNormalizedText().contains(text)) {
174                 final String msg = "The element with ID '" + id + "' does not contain the text '" + text + "'.";
175                 throw new AssertionError(msg);
176             }
177         }
178         catch (final ElementNotFoundException e) {
179             final String msg = "Unable to verify that the element with ID '" + id + "' contains the text '" + text
180                             + "' because the specified element does not exist.";
181             throw new AssertionError(msg, e);
182         }
183     }
184 
185     /**
186      * Verifies that the specified page does not contain the specified text.
187      *
188      * @param page the page to check
189      * @param text the text to check for
190      */
191     public static void assertTextNotPresent(final HtmlPage page, final String text) {
192         if (page.asNormalizedText().contains(text)) {
193             final String msg = "The page contains the text '" + text + "'.";
194             throw new AssertionError(msg);
195         }
196     }
197 
198     /**
199      * Verifies that the element on the specified page which matches the specified ID does not
200      * contain the specified text.
201      *
202      * @param page the page to check
203      * @param text the text to check for
204      * @param id the ID of the element which is expected to not contain the specified text
205      */
206     public static void assertTextNotPresentInElement(final HtmlPage page, final String text, final String id) {
207         try {
208             final HtmlElement element = page.getHtmlElementById(id);
209             if (element.asNormalizedText().contains(text)) {
210                 final String msg = "The element with ID '" + id + "' contains the text '" + text + "'.";
211                 throw new AssertionError(msg);
212             }
213         }
214         catch (final ElementNotFoundException e) {
215             final String msg = "Unable to verify that the element with ID '" + id + "' does not contain the text '"
216                             + text + "' because the specified element does not exist.";
217             throw new AssertionError(msg);
218         }
219     }
220 
221     /**
222      * Verifies that the specified page contains a link with the specified ID.
223      *
224      * @param page the page to check
225      * @param id the ID of the link which the page is expected to contain
226      */
227     public static void assertLinkPresent(final HtmlPage page, final String id) {
228         try {
229             page.getDocumentElement().getOneHtmlElementByAttribute("a", DomElement.ID_ATTRIBUTE, id);
230         }
231         catch (final ElementNotFoundException e) {
232             final String msg = "The page does not contain a link with ID '" + id + "'.";
233             throw new AssertionError(msg, e);
234         }
235     }
236 
237     /**
238      * Verifies that the specified page does not contain a link with the specified ID.
239      *
240      * @param page the page to check
241      * @param id the ID of the link which the page is expected to not contain
242      */
243     public static void assertLinkNotPresent(final HtmlPage page, final String id) {
244         try {
245             page.getDocumentElement().getOneHtmlElementByAttribute("a", DomElement.ID_ATTRIBUTE, id);
246             // Not expected.
247             final String msg = "The page contains a link with ID '" + id + "'.";
248             throw new AssertionError(msg);
249         }
250         catch (final ElementNotFoundException expected) {
251         }
252     }
253 
254     /**
255      * Verifies that the specified page contains a link with the specified text. The specified text
256      * may be a substring of the entire text contained by the link.
257      *
258      * @param page the page to check
259      * @param text the text which a link in the specified page is expected to contain
260      */
261     public static void assertLinkPresentWithText(final HtmlPage page, final String text) {
262         boolean found = false;
263         for (final HtmlAnchor a : page.getAnchors()) {
264             if (a.asNormalizedText().contains(text)) {
265                 found = true;
266                 break;
267             }
268         }
269         if (!found) {
270             final String msg = "The page does not contain a link with text '" + text + "'.";
271             throw new AssertionError(msg);
272         }
273     }
274 
275     /**
276      * Verifies that the specified page does not contain a link with the specified text. The
277      * specified text may be a substring of the entire text contained by the link.
278      *
279      * @param page the page to check
280      * @param text the text which a link in the specified page is not expected to contain
281      */
282     public static void assertLinkNotPresentWithText(final HtmlPage page, final String text) {
283         boolean found = false;
284         for (final HtmlAnchor a : page.getAnchors()) {
285             if (a.asNormalizedText().contains(text)) {
286                 found = true;
287                 break;
288             }
289         }
290         if (found) {
291             final String msg = "The page contains a link with text '" + text + "'.";
292             throw new AssertionError(msg);
293         }
294     }
295 
296     /**
297      * Verifies that the specified page contains a form with the specified name.
298      *
299      * @param page the page to check
300      * @param name the expected name of a form on the page
301      */
302     public static void assertFormPresent(final HtmlPage page, final String name) {
303         try {
304             page.getFormByName(name);
305         }
306         catch (final ElementNotFoundException e) {
307             final String msg = "The page does not contain a form named '" + name + "'.";
308             throw new AssertionError(msg, e);
309         }
310     }
311 
312     /**
313      * Verifies that the specified page does not contain a form with the specified name.
314      *
315      * @param page the page to check
316      * @param name the name of a form which should not exist on the page
317      */
318     public static void assertFormNotPresent(final HtmlPage page, final String name) {
319         try {
320             page.getFormByName(name);
321         }
322         catch (final ElementNotFoundException e) {
323             return;
324         }
325         final String msg = "The page contains a form named '" + name + "'.";
326         throw new AssertionError(msg);
327     }
328 
329     /**
330      * Verifies that the specified page contains an input element with the specified name.
331      *
332      * @param page the page to check
333      * @param name the name of the input element to look for
334      */
335     public static void assertInputPresent(final HtmlPage page, final String name) {
336         final String xpath = "//input[@name='" + name + "']";
337         final List<?> list = page.getByXPath(xpath);
338         if (list.isEmpty()) {
339             throw new AssertionError("Unable to find an input element named '" + name + "'.");
340         }
341     }
342 
343     /**
344      * Verifies that the specified page does not contain an input element with the specified name.
345      *
346      * @param page the page to check
347      * @param name the name of the input element to look for
348      */
349     public static void assertInputNotPresent(final HtmlPage page, final String name) {
350         final String xpath = "//input[@name='" + name + "']";
351         final List<?> list = page.getByXPath(xpath);
352         if (!list.isEmpty()) {
353             throw new AssertionError("Unable to find an input element named '" + name + "'.");
354         }
355     }
356 
357     /**
358      * Verifies that the input element with the specified name on the specified page contains the
359      * specified value.
360      *
361      * @param page the page to check
362      * @param name the name of the input element to check
363      * @param value the value to check for
364      */
365     public static void assertInputContainsValue(final HtmlPage page, final String name, final String value) {
366         final String xpath = "//input[@name='" + name + "']";
367         final List<?> list = page.getByXPath(xpath);
368         if (list.isEmpty()) {
369             throw new AssertionError("Unable to find an input element named '" + name + "'.");
370         }
371         final HtmlInput input = (HtmlInput) list.get(0);
372         final String s = input.getValue();
373         if (!s.equals(value)) {
374             throw new AssertionError("The input element named '" + name + "' contains the value '" + s
375                             + "', not the expected value '" + value + "'.");
376         }
377     }
378 
379     /**
380      * Verifies that the input element with the specified name on the specified page does not
381      * contain the specified value.
382      *
383      * @param page the page to check
384      * @param name the name of the input element to check
385      * @param value the value to check for
386      */
387     public static void assertInputDoesNotContainValue(final HtmlPage page, final String name, final String value) {
388         final String xpath = "//input[@name='" + name + "']";
389         final List<?> list = page.getByXPath(xpath);
390         if (list.isEmpty()) {
391             throw new AssertionError("Unable to find an input element named '" + name + "'.");
392         }
393         final HtmlInput input = (HtmlInput) list.get(0);
394         final String s = input.getValue();
395         if (s.equals(value)) {
396             throw new AssertionError("The input element named '" + name + "' contains the value '" + s
397                             + "', not the expected value '" + value + "'.");
398         }
399     }
400 
401     /**
402      * <p>Many HTML elements are "tabbable" and can have a <code>tabindex</code> attribute
403      * that determines the order in which the components are navigated when
404      * pressing the tab key. To ensure good usability for keyboard navigation,
405      * all tabbable elements should have the <code>tabindex</code> attribute set.</p>
406      *
407      * <p>This method verifies that all tabbable elements have a valid value set for
408      * the <code>tabindex</code> attribute.</p>
409      *
410      * @param page the page to check
411      */
412     public static void assertAllTabIndexAttributesSet(final HtmlPage page) {
413         final List<String> tags =
414             Arrays.asList("a", "area", "button", "input", "object", "select", "textarea");
415 
416         for (final String tag : tags) {
417             for (final HtmlElement element : page.getDocumentElement().getStaticElementsByTagName(tag)) {
418                 final Short tabIndex = element.getTabIndex();
419                 if (tabIndex == null || HtmlElement.TAB_INDEX_OUT_OF_BOUNDS.equals(tabIndex)) {
420                     final String s = element.getAttributeDirect("tabindex");
421                     throw new AssertionError("Illegal value for tab index: '" + s + "'.");
422                 }
423             }
424         }
425     }
426 
427     /**
428      * Many HTML components can have an <code>accesskey</code> attribute which defines a hot key for
429      * keyboard navigation. This method verifies that all the <code>accesskey</code> attributes on the
430      * specified page are unique.
431      *
432      * @param page the page to check
433      */
434     public static void assertAllAccessKeyAttributesUnique(final HtmlPage page) {
435         final List<String> list = new ArrayList<>();
436         for (final HtmlElement element : page.getHtmlElementDescendants()) {
437             final String key = element.getAttributeDirect("accesskey");
438             if (key != null && !key.isEmpty()) {
439                 if (list.contains(key)) {
440                     throw new AssertionError("The access key '" + key + "' is not unique.");
441                 }
442                 list.add(key);
443             }
444         }
445     }
446 
447     /**
448      * Verifies that all element IDs in the specified page are unique.
449      *
450      * @param page the page to check
451      */
452     public static void assertAllIdAttributesUnique(final HtmlPage page) {
453         final List<String> list = new ArrayList<>();
454         for (final HtmlElement element : page.getHtmlElementDescendants()) {
455             final String id = element.getId();
456             if (id != null && !id.isEmpty()) {
457                 if (list.contains(id)) {
458                     throw new AssertionError("The element ID '" + id + "' is not unique.");
459                 }
460                 list.add(id);
461             }
462         }
463     }
464 
465     /**
466      * Assert that the specified parameter is not null. Throw a NullPointerException
467      * if a null is found.
468      * @param description the description to pass into the NullPointerException
469      * @param object the object to check for null
470      */
471     public static void notNull(final String description, final Object object) {
472         if (object == null) {
473             throw new NullPointerException(description);
474         }
475     }
476 }