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.html;
16  
17  import static java.nio.charset.StandardCharsets.ISO_8859_1;
18  import static java.nio.charset.StandardCharsets.UTF_8;
19  
20  import java.io.IOException;
21  import java.net.MalformedURLException;
22  import java.net.URL;
23  import java.nio.charset.StandardCharsets;
24  import java.util.ArrayList;
25  import java.util.Arrays;
26  import java.util.Collections;
27  import java.util.Iterator;
28  import java.util.LinkedList;
29  import java.util.List;
30  import java.util.Set;
31  
32  import javax.xml.parsers.DocumentBuilder;
33  import javax.xml.parsers.DocumentBuilderFactory;
34  
35  import org.apache.commons.io.IOUtils;
36  import org.apache.commons.lang3.SerializationUtils;
37  import org.htmlunit.CollectingAlertHandler;
38  import org.htmlunit.ElementNotFoundException;
39  import org.htmlunit.FailingHttpStatusCodeException;
40  import org.htmlunit.HttpMethod;
41  import org.htmlunit.ImmediateRefreshHandler;
42  import org.htmlunit.IncorrectnessListener;
43  import org.htmlunit.MockWebConnection;
44  import org.htmlunit.OnbeforeunloadHandler;
45  import org.htmlunit.Page;
46  import org.htmlunit.SimpleWebTestCase;
47  import org.htmlunit.StringWebResponse;
48  import org.htmlunit.WaitingRefreshHandler;
49  import org.htmlunit.WebClient;
50  import org.htmlunit.WebRequest;
51  import org.htmlunit.WebResponse;
52  import org.htmlunit.html.HtmlElementTest.HtmlAttributeChangeListenerTestImpl;
53  import org.htmlunit.junit.annotation.Alerts;
54  import org.htmlunit.util.Cookie;
55  import org.htmlunit.util.MimeType;
56  import org.htmlunit.util.NameValuePair;
57  import org.htmlunit.util.StringUtils;
58  import org.junit.jupiter.api.Assertions;
59  import org.junit.jupiter.api.Test;
60  import org.w3c.dom.NodeList;
61  
62  /**
63   * Tests for {@link HtmlPage}.
64   *
65   * @author Mike Bowler
66   * @author Noboru Sinohara
67   * @author David K. Taylor
68   * @author Andreas Hangler
69   * @author Christian Sell
70   * @author Marc Guillemot
71   * @author Ahmed Ashour
72   * @author Frank Danek
73   * @author Ronald Brill
74   */
75  public class HtmlPageTest extends SimpleWebTestCase {
76  
77      /** The doctype prefix for standards mode. */
78      public static final String STANDARDS_MODE_PREFIX_
79          = "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">\n";
80  
81      /**
82       * @throws Exception if the test fails
83       */
84      @Test
85      public void formSubmit() throws Exception {
86          final String htmlContent = DOCTYPE_HTML
87              + "<html>\n"
88              + "<head><title>foo</title></head>\n"
89              + "<body>\n"
90              + "<p>hello world</p>\n"
91              + "<form id='form1' action='/formSubmit' method='PoSt'>\n"
92              + "<input type='text' NAME='textInput1' value='textInput1'/>\n"
93              + "<input type='text' name='textInput2' value='textInput2'/>\n"
94              + "<input type='hidden' name='hidden1' value='hidden1'/>\n"
95              + "<input type='submit' name='submitInput1' value='push me'/>\n"
96              + "</form>\n"
97              + "</body></html>";
98          final HtmlPage page = loadPage(htmlContent);
99          final MockWebConnection webConnection = getMockConnection(page);
100 
101         final HtmlForm form = page.getHtmlElementById("form1");
102         final HtmlInput textInput = form.getInputByName("textInput1");
103         textInput.setValue("foo");
104 
105         final HtmlSubmitInput button = form.getInputByName("submitInput1");
106         final HtmlPage secondPage = button.click();
107 
108         final List<NameValuePair> expectedParameters = new ArrayList<>();
109         expectedParameters.add(new NameValuePair("textInput1", "foo"));
110         expectedParameters.add(new NameValuePair("textInput2", "textInput2"));
111         expectedParameters.add(new NameValuePair("hidden1", "hidden1"));
112         expectedParameters.add(new NameValuePair("submitInput1", "push me"));
113 
114         final URL expectedUrl = new URL(URL_FIRST, "formSubmit");
115         final URL actualUrl = secondPage.getUrl();
116         assertEquals("url", expectedUrl, actualUrl);
117         assertSame("method", HttpMethod.POST, webConnection.getLastMethod());
118         assertEquals("parameters", expectedParameters, webConnection.getLastParameters());
119         assertNotNull(secondPage);
120     }
121 
122     /**
123      * @throws Exception if the test fails
124      */
125     @Test
126     public void getElementByIdNull() throws Exception {
127         final String htmlContent = DOCTYPE_HTML
128             + "<html>\n"
129             + "<head><title>foo</title></head>\n"
130             + "<body>\n"
131             + "<p>hello world</p>\n"
132             + "</body></html>";
133 
134         final HtmlPage page = loadPage(htmlContent);
135 
136         assertNull(page.getElementById(null));
137     }
138 
139     /**
140      * @throws Exception if the test fails
141      */
142     @Test
143     public void getElementsByIdNull() throws Exception {
144         final String htmlContent = DOCTYPE_HTML
145             + "<html>\n"
146             + "<head><title>foo</title></head>\n"
147             + "<body>\n"
148             + "<p>hello world</p>\n"
149             + "</body></html>";
150 
151         final HtmlPage page = loadPage(htmlContent);
152 
153         final List<DomElement> elements = page.getElementsById(null);
154         assertNotNull(elements);
155         assertEquals(0, elements.size());
156     }
157 
158     /**
159      * @throws Exception if the test fails
160      */
161     @Test
162     public void getElementsByIdOrNameNull() throws Exception {
163         final String htmlContent = DOCTYPE_HTML
164             + "<html>\n"
165             + "<head><title>foo</title></head>\n"
166             + "<body>\n"
167             + "<p>hello world</p>\n"
168             + "</body></html>";
169 
170         final HtmlPage page = loadPage(htmlContent);
171 
172         final List<DomElement> elements = page.getElementsByIdAndOrName(null);
173         assertNotNull(elements);
174         assertEquals(0, elements.size());
175     }
176 
177     /**
178      * @throws Exception if the test fails
179      */
180     @Test
181     public void getElementByNameNull() throws Exception {
182         final String htmlContent = DOCTYPE_HTML
183             + "<html>\n"
184             + "<head><title>foo</title></head>\n"
185             + "<body>\n"
186             + "<p>hello world</p>\n"
187             + "</body></html>";
188 
189         final HtmlPage page = loadPage(htmlContent);
190 
191         Assertions.assertThrows(ElementNotFoundException.class, () -> page.getElementByName(null));
192     }
193 
194     /**
195      * @throws Exception if the test fails
196      */
197     @Test
198     public void getElementsByNameNull() throws Exception {
199         final String htmlContent = DOCTYPE_HTML
200             + "<html>\n"
201             + "<head><title>foo</title></head>\n"
202             + "<body>\n"
203             + "<p>hello world</p>\n"
204             + "</body></html>";
205 
206         final HtmlPage page = loadPage(htmlContent);
207 
208         final List<DomElement> elements = page.getElementsByName(null);
209         assertNotNull(elements);
210         assertEquals(0, elements.size());
211     }
212 
213     /**
214      * Tests getHtmlElement() for all elements that can be loaded.
215      * @throws Exception if the test fails
216      */
217     @Test
218     public void getHtmlElement() throws Exception {
219         final String htmlContent = DOCTYPE_HTML
220             + "<html>\n"
221             + "<head><title>foo</title></head>\n"
222             + "<body>\n"
223             + "  <p>hello world</p>\n"
224             + "  <form id='form1' id='form1' action='/formSubmit' method='post'>\n"
225             + "  <input type='text' NAME='textInput1' value='textInput1'/>\n"
226             + "  <button type='submit' name='button1'>foobar</button>\n"
227             + "  <select name='select1'>\n"
228             + "    <option value='option1'>Option1</option>\n"
229             + "  </select>\n"
230             + "  <textarea name='textArea1'>foobar</textarea>\n"
231             + "  </form>\n"
232             + "  <a href='http://www.foo.com' name='anchor1'>foo.com</a>\n"
233             + "  <table id='table1'>\n"
234             + "    <tr>\n"
235             + "      <th id='header1'>Header</th>\n"
236             + "      <td id='data1'>Data</td>\n"
237             + "    </tr>\n"
238             + "  </table>\n"
239             + "</body></html>";
240         final HtmlPage page = loadPage(htmlContent);
241 
242         final HtmlForm form = page.getHtmlElementById("form1");
243         assertSame("form1", form, page.getHtmlElementById("form1")); //huh??
244 
245         final HtmlInput input = form.getInputByName("textInput1");
246         assertSame("input1", input, form.getInputByName("textInput1")); //??
247 
248         final HtmlButton button = form.getButtonByName("button1");
249         assertSame("button1", button, form.getButtonByName("button1"));
250 
251         final HtmlSelect select = form.getSelectsByName("select1").get(0);
252         assertSame("select1", select, form.getSelectsByName("select1").get(0));
253 
254         final HtmlOption option = select.getOptionByValue("option1");
255         assertSame("option1", option, select.getOptionByValue("option1"));
256 
257         final HtmlTable table = page.getHtmlElementById("table1");
258         assertSame("table1", table, page.getHtmlElementById("table1"));
259 
260         final HtmlAnchor anchor = page.getAnchorByName("anchor1");
261         assertSame("anchor1", anchor, page.getAnchorByName("anchor1"));
262         assertSame("anchor3", anchor, page.getAnchorByHref("http://www.foo.com"));
263         assertSame("anchor4", anchor, page.getAnchorByText("foo.com"));
264 
265         final HtmlTableRow tableRow = table.getRow(0);
266         assertSame("tableRow1", tableRow, table.getRow(0));
267 
268         final HtmlTableHeaderCell tableHeaderCell = (HtmlTableHeaderCell) tableRow.getCell(0);
269         assertSame("tableHeaderCell1", tableHeaderCell, tableRow.getCell(0));
270         assertSame("tableHeaderCell2", tableHeaderCell, page.getHtmlElementById("header1"));
271 
272         final HtmlTableDataCell tableDataCell = (HtmlTableDataCell) tableRow.getCell(1);
273         assertSame("tableDataCell1", tableDataCell, tableRow.getCell(1));
274         assertSame("tableDataCell2", tableDataCell, page.getHtmlElementById("data1"));
275 
276         final HtmlTextArea textArea = form.getTextAreaByName("textArea1");
277         assertSame("textArea1", textArea, form.getTextAreaByName("textArea1"));
278     }
279 
280     /**
281      * Tests getHtmlElement() for all elements that can be loaded.
282      * @throws Exception if the test fails
283      */
284     @Test
285     public void getAnchorByText() throws Exception {
286         final String htmlContent = DOCTYPE_HTML
287             + "<html>\n"
288             + "<head></head>\n"
289             + "<body>\n"
290             + "  <a href='http://www.foo.com' id='anchor1'>anchor text</a>\n"
291             + "  <a href='http://www.foo.com' id='anchor2'><span>anchor text inside span</span></a>\n"
292             + "  <a href='http://www.foo.com' id='anchor3'>"
293                 + "<svg><rect x='1' y='11' width='8' height='8'/></svg>"
294                 + "<span>complex</span>"
295                 + "</a>\n"
296             + "</body></html>";
297         final HtmlPage page = loadPage(htmlContent);
298 
299         assertSame("anchor1", page.getElementById("anchor1"), page.getAnchorByText("anchor text"));
300         assertSame("anchor2", page.getElementById("anchor2"), page.getAnchorByText("anchor text inside span"));
301         assertSame("anchor3", page.getElementById("anchor3"), page.getAnchorByText("complex"));
302     }
303 
304     /**
305      * @throws Exception if the test fails
306      */
307     @Test
308     public void getTabbableElements_None() throws Exception {
309         final String htmlContent = DOCTYPE_HTML
310             + "<html>\n"
311             + "<head><title>foo</title></head>\n"
312             + "<body>\n"
313             + "<p>hello world</p>\n"
314             + "<table><tr><td>foo</td></tr></table>\n"
315             + "</body></html>";
316 
317         final HtmlPage page = loadPage(htmlContent);
318 
319         assertEquals(Collections.EMPTY_LIST, page.getTabbableElements());
320     }
321 
322     /**
323      * @throws Exception if the test fails
324      */
325     @Test
326     public void getTabbableElements_OneEnabled_OneDisabled() throws Exception {
327         final String htmlContent = DOCTYPE_HTML
328             + "<html>\n"
329             + "<head><title>foo</title></head>\n"
330             + "<body>\n"
331             + "<form><p>hello world</p>\n"
332             + "<input name='foo' type='submit' disabled='disabled' id='foo'/>\n"
333             + "<input name='bar' type='submit' id='bar'/>\n"
334             + "</form></body></html>";
335         final HtmlPage page = loadPage(htmlContent);
336 
337         final List<HtmlElement> expectedElements = new ArrayList<>();
338         expectedElements.add(page.getHtmlElementById("bar"));
339 
340         assertEquals(expectedElements, page.getTabbableElements());
341     }
342 
343     /**
344      * @throws Exception if the test fails
345      */
346     @Test
347     public void getTabbableElements() throws Exception {
348         final String htmlContent = DOCTYPE_HTML
349             + "<html>\n"
350             + "<head><title>foo</title></head>\n"
351             + "<body>\n"
352             + "<a id='a' tabindex='1'>foo</a>\n"
353             + "<a id='b'>foo</a>\n"
354             + "<form>\n"
355             + "<a id='c' tabindex='3'>foo</a>\n"
356             + "<a id='d' tabindex='2'>foo</a>\n"
357             + "<a id='e' tabindex='0'>foo</a>\n"
358             + "</form>\n"
359             + "<a id='f' tabindex='3'>foo</a>\n"
360             + "<a id='g' tabindex='1'>foo</a>\n"
361             + "<a id='q' tabindex='-1'>foo</a>\n"
362             + "<form><p>hello world</p>\n"
363             + "<input name='foo' type='submit' disabled='disabled' id='foo'/>\n"
364             + "<input name='bar' type='submit' id='bar'/>\n"
365             + "</form></body></html>";
366         final HtmlPage page = loadPage(htmlContent);
367 
368         final List<HtmlElement> expectedElements = Arrays.asList(new HtmlElement[] {page.getHtmlElementById("a"),
369                 page.getHtmlElementById("g"), page.getHtmlElementById("d"),
370                 page.getHtmlElementById("c"), page.getHtmlElementById("f"),
371                 page.getHtmlElementById("e"), page.getHtmlElementById("b"),
372                 page.getHtmlElementById("bar")});
373 
374         assertEquals(expectedElements, page.getTabbableElements());
375 
376         final String[] expectedIds = {"a", "g", "d", "c", "f", "e", "b", "bar"};
377         assertEquals(expectedIds, page.getTabbableElementIds());
378     }
379 
380     /**
381      * @throws Exception if the test fails
382      */
383     @Test
384     public void getHtmlElementByAccessKey() throws Exception {
385         final String htmlContent = DOCTYPE_HTML
386             + "<html>\n"
387             + "<head><title>foo</title></head>\n"
388             + "<body>\n"
389             + "<a id='a' accesskey='a'>foo</a>\n"
390             + "<a id='b'>foo</a>\n"
391             + "<form>\n"
392             + "<a id='c' accesskey='c'>foo</a>\n"
393             + "</form>\n"
394             + "<form><p>hello world</p>\n"
395             + "<input name='foo' type='submit' disabled='disabled' id='foo' accesskey='f'/>\n"
396             + "<input name='bar' type='submit' id='bar'/>\n"
397             + "</form></body></html>";
398         final HtmlPage page = loadPage(htmlContent);
399 
400         assertEquals(page.getHtmlElementById("a"), page.getHtmlElementByAccessKey('A'));
401         assertEquals(page.getHtmlElementById("c"), page.getHtmlElementByAccessKey('c'));
402         assertNull(page.getHtmlElementByAccessKey('z'));
403     }
404 
405     /**
406      * @throws Exception if the test fails
407      */
408     @Test
409     public void getHtmlElementsByAccessKey() throws Exception {
410         final String htmlContent = DOCTYPE_HTML
411             + "<html>\n"
412             + "<head><title>foo</title></head><body>\n"
413             + "<a id='a' accesskey='a'>foo</a>\n"
414             + "<a id='b' accesskey='a'>foo</a>\n"
415             + "<form>\n"
416             + "<a id='c' accesskey='c'>foo</a>\n"
417             + "</form></body></html>";
418         final HtmlPage page = loadPage(htmlContent);
419 
420         final List<HtmlElement> expectedElements = Arrays.asList(new HtmlElement[] {page.getHtmlElementById("a"),
421                 page.getHtmlElementById("b")});
422         final List<HtmlElement> collectedElements = page.getHtmlElementsByAccessKey('a');
423         assertEquals(expectedElements, collectedElements);
424     }
425 
426     /**
427      * @throws Exception if the test fails
428      */
429     @Test
430     public void getFullQualifiedUrl_NoBaseSpecified() throws Exception {
431         final String htmlContent = DOCTYPE_HTML
432             + "<html><head><title>foo</title></head><body>\n"
433             + "<form id='form1'>\n"
434             + "<table><tr><td><input type='text' id='foo'/></td></tr></table>\n"
435             + "</form></body></html>";
436         final WebClient client = getWebClient();
437 
438         final MockWebConnection webConnection = new MockWebConnection();
439         webConnection.setDefaultResponse(htmlContent);
440         client.setWebConnection(webConnection);
441 
442         final String urlString = URL_FIRST.toExternalForm();
443         final HtmlPage page = client.getPage(URL_FIRST);
444 
445         assertEquals(urlString, page.getFullyQualifiedUrl(""));
446         assertEquals(urlString + "foo", page.getFullyQualifiedUrl("foo"));
447         assertEquals("http://foo.com/bar", page.getFullyQualifiedUrl("http://foo.com/bar"));
448         assertEquals("mailto:me@foo.com", page.getFullyQualifiedUrl("mailto:me@foo.com"));
449 
450         assertEquals(urlString + "foo", page.getFullyQualifiedUrl("foo"));
451         assertEquals(urlString + "bbb", page.getFullyQualifiedUrl("aaa/../bbb"));
452         assertEquals(urlString + "c/d", page.getFullyQualifiedUrl("c/./d"));
453 
454         final HtmlPage secondPage = client.getPage(urlString + "foo/bar?a=b&c=d");
455         assertEquals(urlString + "foo/bar?a=b&c=d", secondPage.getFullyQualifiedUrl(""));
456         assertEquals(urlString + "foo/one", secondPage.getFullyQualifiedUrl("one"));
457         assertEquals(urlString + "two", secondPage.getFullyQualifiedUrl("/two"));
458         assertEquals(urlString + "foo/two?a=b", secondPage.getFullyQualifiedUrl("two?a=b"));
459 
460         final HtmlPage thirdPage = client.getPage("http://foo.com/dog/cat/one.html");
461         assertEquals("http://foo.com/dog/cat/one.html", thirdPage.getFullyQualifiedUrl(""));
462         assertEquals("http://foo.com/dog/cat/two.html", thirdPage.getFullyQualifiedUrl("two.html"));
463     }
464 
465     /**
466      * @throws Exception if the test fails
467      */
468     @Test
469     public void getFullQualifiedUrl_WithBase() throws Exception {
470         testGetFullQualifiedUrl_WithBase("http", "");
471         testGetFullQualifiedUrl_WithBase("http", ":8080");
472         testGetFullQualifiedUrl_WithBase("https", "");
473         testGetFullQualifiedUrl_WithBase("https", ":2005");
474     }
475 
476     /**
477      * @throws Exception if the test fails
478      */
479     private void testGetFullQualifiedUrl_WithBase(final String baseProtocol, final String basePortPart)
480         throws Exception {
481 
482         final String baseUrl = baseProtocol + "://second" + basePortPart;
483         final String htmlContent = DOCTYPE_HTML
484             + "<html><head><title>foo</title>\n"
485             + "<base href='" + baseUrl + "'>\n"
486             + "</head><body>\n"
487             + "<form id='form1'>\n"
488             + "<table><tr><td><input type='text' id='foo'/></td></tr></table>\n"
489             + "</form></body></html>";
490         final HtmlPage page = loadPage(htmlContent);
491 
492         assertEquals(baseUrl, page.getFullyQualifiedUrl(""));
493         assertEquals(baseUrl + "/foo", page.getFullyQualifiedUrl("foo"));
494         assertEquals(baseUrl + "/foo.js", page.getFullyQualifiedUrl("/foo.js"));
495         assertEquals("http://foo.com/bar", page.getFullyQualifiedUrl("http://foo.com/bar"));
496         assertEquals("mailto:me@foo.com", page.getFullyQualifiedUrl("mailto:me@foo.com"));
497 
498         assertEquals(baseUrl + "/bbb", page.getFullyQualifiedUrl("aaa/../bbb"));
499         assertEquals(baseUrl + "/c/d", page.getFullyQualifiedUrl("c/./d"));
500     }
501 
502     /**
503      * @throws Exception if the test fails
504      */
505     @Test
506     public void getFullQualifiedUrl_invalid() throws Exception {
507         final String htmlContent = DOCTYPE_HTML + "<html><body></body></html>";
508         final HtmlPage page = loadPage(htmlContent);
509 
510         Assertions.assertThrows(MalformedURLException.class, () -> page.getFullyQualifiedUrl("http://"));
511     }
512 
513     /**
514      * @throws Exception if an error occurs
515      */
516     @Test
517     public void testBase_Multiple() throws Exception {
518         final String html = DOCTYPE_HTML
519             + "<html>\n"
520             + "<head>\n"
521             + "<base href='" + URL_SECOND + "'>\n"
522             + "<base href='" + URL_THIRD + "'>\n"
523             + "</head>\n"
524             + "<body>\n"
525             + "  <a href='somepage.html'>\n"
526             + "</body></html>";
527 
528         final WebClient webClient = getWebClient();
529         final List<String> collectedIncorrectness = new ArrayList<>();
530         final IncorrectnessListener listener = new IncorrectnessListener() {
531             @Override
532             public void notify(final String message, final Object origin) {
533                 collectedIncorrectness.add(message);
534             }
535         };
536         webClient.setIncorrectnessListener(listener);
537 
538         final MockWebConnection webConnection = new MockWebConnection();
539         webClient.setWebConnection(webConnection);
540         webConnection.setDefaultResponse(html);
541         final HtmlPage page = webClient.getPage(URL_FIRST);
542         page.getAnchors().get(0).click();
543 
544         final String[] expectedIncorrectness = {"Multiple 'base' detected, only the first is used.",
545             "Multiple 'base' detected, only the first is used."};
546         assertEquals(expectedIncorrectness, collectedIncorrectness);
547     }
548 
549     /**
550      * @throws Exception if an error occurs
551      */
552     @Test
553     public void testBase_InsideBody() throws Exception {
554         final String html = DOCTYPE_HTML
555             + "<html><body>\n"
556             + "  <base href='" + URL_SECOND + "'>\n"
557             + "  <a href='somepage.html'>link</a>\n"
558             + "</body></html>";
559 
560         final WebClient webClient = getWebClient();
561 
562         final MockWebConnection webConnection = new MockWebConnection();
563         webClient.setWebConnection(webConnection);
564         webConnection.setDefaultResponse(html);
565         final HtmlPage page = webClient.getPage(URL_FIRST);
566         final HtmlAnchor anchor = page.getAnchors().get(0);
567         final HtmlPage secondPage = anchor.click();
568 
569         assertEquals(URL_SECOND + "somepage.html", secondPage.getUrl());
570     }
571 
572     /**
573      * @throws Exception if the test fails
574      */
575     @Test
576     public void onLoadHandler_BodyStatement() throws Exception {
577         final String htmlContent = DOCTYPE_HTML
578             + "<html><head><title>foo</title>\n"
579             + "</head><body onLoad='alert(\"foo\")'>\n"
580             + "</body></html>";
581         final List<String> collectedAlerts = new ArrayList<>();
582         final HtmlPage page = loadPage(htmlContent, collectedAlerts);
583         assertEquals("foo", page.getTitleText());
584 
585         final String[] expectedAlerts = {"foo"};
586         assertEquals(expectedAlerts, collectedAlerts);
587     }
588 
589     /**
590      * If the onload handler contains two statements then only the first would execute.
591      * @throws Exception if the test fails
592      */
593     @Test
594     public void onLoadHandler_TwoBodyStatements() throws Exception {
595         final String htmlContent = DOCTYPE_HTML
596             + "<html><head><title>foo</title>\n"
597             + "</head><body onLoad='alert(\"foo\");alert(\"bar\")'>\n"
598             + "</body></html>";
599         final List<String> collectedAlerts = new ArrayList<>();
600         final HtmlPage page = loadPage(htmlContent, collectedAlerts);
601         assertEquals("foo", page.getTitleText());
602 
603         final String[] expectedAlerts = {"foo", "bar"};
604         assertEquals(expectedAlerts, collectedAlerts);
605     }
606 
607     /**
608      * Regression test for bug #69.
609      * @throws Exception if the test fails
610      */
611     @Test
612     public void onLoadHandler_BodyName() throws Exception {
613         final String htmlContent = DOCTYPE_HTML
614             + "<html><head><title>foo</title>\n"
615             + "<script type='text/javascript'>\n"
616             + "  window.onload = function() {alert('foo')}</script>\n"
617             + "</head><body></body></html>";
618         final List<String> collectedAlerts = new ArrayList<>();
619         final HtmlPage page = loadPage(htmlContent, collectedAlerts);
620         assertEquals("foo", page.getTitleText());
621 
622         final String[] expectedAlerts = {"foo"};
623         assertEquals(expectedAlerts, collectedAlerts);
624     }
625 
626     /**
627      * Regression test for bug #69.
628      * @throws Exception if the test fails
629      */
630     @Test
631     public void onLoadHandler_BodyName_NotAFunction() throws Exception {
632         final String htmlContent = DOCTYPE_HTML
633             + "<html><head><title>foo</title></head>\n"
634             + "<body onLoad='foo=4711'>\n"
635             + "<a name='alert' href='javascript:alert(foo)'/>\n"
636             + "</body></html>";
637         final List<String> collectedAlerts = new ArrayList<>();
638         final HtmlPage page = loadPage(htmlContent, collectedAlerts);
639         assertEquals("foo", page.getTitleText());
640 
641         page.getAnchorByName("alert").click();
642 
643         final String[] expectedAlerts = {"4711"};
644         assertEquals(expectedAlerts, collectedAlerts);
645     }
646 
647     /**
648      * Regression test for window.onload property.
649      * @throws Exception if the test fails
650      */
651     @Test
652     public void onLoadHandler_ScriptName() throws Exception {
653         final String htmlContent = DOCTYPE_HTML
654             + "<html><head><title>foo</title>\n"
655             + "<script type='text/javascript'>\n"
656             + "load=function() {alert('foo')};\n"
657             + "onload=load\n"
658             + "</script></head><body></body></html>";
659         final List<String> collectedAlerts = new ArrayList<>();
660         final HtmlPage page = loadPage(htmlContent, collectedAlerts);
661         assertEquals("foo", page.getTitleText());
662 
663         final String[] expectedAlerts = {"foo"};
664         assertEquals(expectedAlerts, collectedAlerts);
665     }
666 
667     /**
668      * @throws Exception if the test fails
669      */
670     @Test
671     public void embeddedMetaTag_Regression() throws Exception {
672         final String htmlContent = DOCTYPE_HTML
673             + "<html><head><title>foo</title>\n"
674             + "</head><body>\n"
675             + "<table><tr><td>\n"
676             + "<meta name=vs_targetSchema content=\"http://schemas.microsoft.com/intellisense/ie5\">\n"
677             + "<form name='form1'>\n"
678             + "  <input type='text' name='textfield1' id='textfield1' value='foo' />\n"
679             + "  <input type='text' name='textfield2' id='textfield2'/>\n"
680             + "</form>\n"
681             + "</td></tr></table>\n"
682             + "</body></html>";
683         final List<String> collectedAlerts = new ArrayList<>();
684 
685         // This used to blow up on page load
686         final HtmlPage page = loadPage(htmlContent, collectedAlerts);
687         assertEquals("foo", page.getTitleText());
688 
689         final List<?> expectedAlerts = Collections.EMPTY_LIST;
690         assertEquals(expectedAlerts, collectedAlerts);
691     }
692 
693     /**
694      * Verifies that an empty charset in a content-type meta tag is ignored. See bug #752.
695      * @throws Exception if an error occurs
696      */
697     @Test
698     public void getPageEncoding_EmptyCharset() throws Exception {
699         final String html = DOCTYPE_HTML
700             + "<html><head>\n"
701             + "<meta http-equiv='Content-Type' content='text/html; charset='>\n"
702             + "</head><body>abc</body></html>";
703         final HtmlPage page = loadPage(html);
704         assertEquals(ISO_8859_1, page.getCharset());
705     }
706 
707     /**
708      * Bug #914.
709      *
710      * @throws Exception if an error occurs
711      */
712     @Test
713     public void getPageEncoding_HeaderHasPrecedenceOverMetaTag() throws Exception {
714         final String html = DOCTYPE_HTML
715             + "<html><head><meta content='text/html; charset=iso-8859-1' http-equiv='Content-Type'/>\n"
716             + "</head><body></body></html>";
717         final MockWebConnection conn = new MockWebConnection();
718         conn.setResponse(URL_FIRST, html, "text/html; charset=UTF-8");
719         final WebClient client = getWebClient();
720         client.setWebConnection(conn);
721         final HtmlPage page = client.getPage(URL_FIRST);
722         assertSame(UTF_8, page.getCharset());
723         assertEquals(page.getWebResponse().getContentCharset(), page.getCharset());
724     }
725 
726     /**
727      * @throws Exception if the test fails
728      */
729     @Test
730     public void getForms() throws Exception {
731         final String htmlContent = DOCTYPE_HTML
732             + "<html>\n"
733             + "<head><title>foo</title></head>\n"
734             + "<body>\n"
735             + "<form name='one'>\n"
736             + "<a id='c' accesskey='c'>foo</a>\n"
737             + "</form>\n"
738             + "<form name='two'>\n"
739             + "<a id='c' accesskey='c'>foo</a>\n"
740             + "</form>\n"
741             + "<input name='foo' type='submit' disabled='disabled' id='foo' accesskey='f'/>\n"
742             + "<input name='bar' type='submit' id='bar'/>\n"
743             + "</form></body></html>";
744 
745         final HtmlPage page = loadPage(htmlContent);
746 
747         final List<HtmlForm> expectedForms = Arrays.asList(new HtmlForm[] {page.getFormByName("one"),
748                 page.getFormByName("two")});
749         assertEquals(expectedForms, page.getForms());
750     }
751 
752     /**
753      * Ensures that if a page is supposed to refresh itself every certain amount of
754      * time, and the ImmediateRefreshHandler is being used, an OOME is avoided by
755      * not performing the refresh.
756      * @throws Exception if the test fails
757      */
758     @Test
759     public void refresh_ImmediateRefresh_AvoidOOME() throws Exception {
760         final String firstContent = DOCTYPE_HTML
761             + "<html><head><title>first</title>\n"
762             + "<META HTTP-EQUIV=\"Refresh\" CONTENT=\"1\">\n"
763             + "</head><body></body></html>";
764 
765         final WebClient client = getWebClient();
766         assertTrue(ImmediateRefreshHandler.class.isInstance(client.getRefreshHandler()));
767         try {
768             loadPage(firstContent);
769             Assertions.fail("should have thrown");
770         }
771         catch (final RuntimeException e) {
772             assertTrue(e.getMessage().indexOf("could have caused an OutOfMemoryError") > -1);
773         }
774         Thread.sleep(1000);
775     }
776 
777     /**
778      * @throws Exception if the test fails
779      */
780     @Test
781     @Alerts("Too many redirects (>= 72) for §§URL§§")
782     public void refresh_EndlessLoop() throws Exception {
783         final String firstContent = DOCTYPE_HTML
784             + "<html><head><title>first</title>\n"
785             + "<META HTTP-EQUIV=\"Refresh\" CONTENT=\"0\">\n"
786             + "</head><body></body></html>";
787 
788         expandExpectedAlertsVariables(URL_FIRST);
789 
790         final WebClient client = getWebClient();
791         client.setRefreshHandler(new WaitingRefreshHandler());
792         try {
793             loadPage(firstContent);
794             Assertions.fail("should have thrown");
795         }
796         catch (final FailingHttpStatusCodeException e) {
797             assertEquals(getExpectedAlerts()[0], e.getMessage());
798         }
799     }
800 
801     /**
802      * Test auto-refresh from a meta tag inside noscript.
803      * @throws Exception if the test fails
804      */
805     @Test
806     public void refresh_MetaTagNoScript() throws Exception {
807         final String firstContent = DOCTYPE_HTML
808             + "<html><head><title>first</title>\n"
809             + "<noscript>\n"
810             + "<META HTTP-EQUIV=\"Refresh\" CONTENT=\"0;URL=" + URL_SECOND + "\">\n"
811             + "</noscript>\n"
812             + "</head><body></body></html>";
813         final String secondContent = DOCTYPE_HTML + "<html><head><title>second</title></head><body></body></html>";
814 
815         final WebClient client = getWebClient();
816 
817         final MockWebConnection webConnection = new MockWebConnection();
818         webConnection.setResponse(URL_FIRST, firstContent);
819         webConnection.setResponse(URL_SECOND, secondContent);
820         client.setWebConnection(webConnection);
821 
822         HtmlPage page = client.getPage(URL_FIRST);
823         assertEquals("first", page.getTitleText());
824 
825         client.getOptions().setJavaScriptEnabled(false);
826         page = client.getPage(URL_FIRST);
827         assertEquals("second", page.getTitleText());
828     }
829 
830     /**
831      * Test auto-refresh from a meta tag with a refresh handler that doesn't refresh.
832      * @throws Exception if the test fails
833      */
834     @Test
835     public void refresh_MetaTag_CustomRefreshHandler() throws Exception {
836         final String firstContent = DOCTYPE_HTML
837             + "<html><head><title>first</title>\n"
838             + "<META HTTP-EQUIV=\"Refresh\" CONTENT=\"3;URL=" + URL_SECOND + "\">\n"
839             + "</head><body></body></html>";
840         final String secondContent = DOCTYPE_HTML + "<html><head><title>second</title></head><body></body></html>";
841 
842         final WebClient client = getWebClient();
843         final List<Object> collectedItems = new ArrayList<>();
844         client.setRefreshHandler(new LoggingRefreshHandler(collectedItems));
845 
846         final MockWebConnection webConnection = new MockWebConnection();
847         webConnection.setResponse(URL_FIRST, firstContent);
848         webConnection.setResponse(URL_SECOND, secondContent);
849         client.setWebConnection(webConnection);
850 
851         final HtmlPage page = client.getPage(URL_FIRST);
852 
853         assertEquals("first", page.getTitleText());
854 
855         // avoid using equals() on URL because it takes to much time (due to ip resolution)
856         assertEquals("first", collectedItems.get(0));
857         assertEquals(URL_SECOND, (URL) collectedItems.get(1));
858         assertEquals(Integer.valueOf(3), collectedItems.get(2));
859     }
860 
861     /**
862      * Test that the parent of the DOM Document (HtmlPage) is null.
863      * @throws Exception if the test fails
864      */
865     @Test
866     public void documentParentIsNull() throws Exception {
867         final String htmlContent = DOCTYPE_HTML
868             + "<html>\n"
869             + "<head><title>foo</title></head>\n"
870             + "<body>\n"
871             + "</body></html>";
872         final HtmlPage page = loadPage(htmlContent);
873 
874         assertNotNull(page);
875         assertNull(page.getParentNode());
876     }
877 
878     /**
879      * @throws Exception if the test fails
880      */
881     @Test
882     public void documentElement() throws Exception {
883         final String htmlContent = DOCTYPE_HTML
884             + "<html>\n"
885             + "<head><title>foo</title></head>\n"
886             + "<body>\n"
887             + "</body></html>";
888         final HtmlPage page = loadPage(htmlContent);
889 
890         final DomElement root = page.getDocumentElement();
891 
892         assertNotNull(root);
893         assertEquals("html", root.getTagName());
894         assertSame(page, root.getParentNode());
895     }
896 
897     /**
898      * @throws Exception if the test fails
899      */
900     @Test
901     public void documentNodeType() throws Exception {
902         final String htmlContent = DOCTYPE_HTML
903             + "<html>\n"
904             + "<head><title>foo</title></head>\n"
905             + "<body>\n"
906             + "</body></html>";
907         final HtmlPage page = loadPage(htmlContent);
908 
909         final DomElement root = page.getDocumentElement();
910 
911         assertEquals(org.w3c.dom.Node.DOCUMENT_NODE, page.getNodeType());
912         assertEquals(org.w3c.dom.Node.ELEMENT_NODE, root.getNodeType());
913         assertEquals("#document", page.getNodeName());
914     }
915 
916     /**
917      * @throws Exception if the test fails
918      */
919     @Test
920     public void deregisterFrameWithoutSrc() throws Exception {
921         final String htmlContent = DOCTYPE_HTML
922             + "<html>\n"
923             + "<head><title>foo</title></head>\n"
924             + "<body>\n"
925             + "<iframe></iframe>\n"
926             + "<a href='about:blank'>link</a>\n"
927             + "</body></html>";
928 
929         final HtmlPage page = loadPage(htmlContent);
930         final HtmlAnchor link = page.getAnchors().get(0);
931         link.click();
932     }
933 
934     /**
935      * Tests that a return statement in onload doesn't throw any exception.
936      * @throws Exception if the test fails
937      */
938     @Test
939     public void onLoadReturn() throws Exception {
940         final String htmlContent = DOCTYPE_HTML
941             + "<html><head><title>foo</title></head>\n"
942             + "<body onload='return true'>\n"
943             + "</body></html>";
944 
945         loadPage(htmlContent);
946     }
947 
948     /**
949      * @exception Exception If the test fails
950      */
951     @Test
952     public void asXml() throws Exception {
953         final String htmlContent =
954             "<html><head><title>foo</title></head>"
955             + "<body><p>helloworld</p></body>"
956             + "</html>";
957 
958         final HtmlPage page = loadPage(htmlContent);
959         String xml = page.asXml();
960         final String prefix = "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>";
961         assertTrue(xml.startsWith(prefix));
962         xml = xml.substring(prefix.length());
963         assertEquals(htmlContent, xml.replaceAll("\\s", ""));
964     }
965 
966     /**
967      * Tests that the generated XML is valid as HTML code too.
968      * @exception Exception If the test fails
969      */
970     @Test
971     public void asXmlValidHtmlOutput() throws Exception {
972         final String html =
973             "<html><head><title>foo</title>"
974             + "<script src='script.js'></script></head>"
975             + "<body><div></div><iframe src='about:blank'></iframe></body>"
976             + "</html>";
977 
978         final WebClient client = getWebClient();
979         final MockWebConnection webConnection = new MockWebConnection();
980         webConnection.setDefaultResponse(html);
981         webConnection.setResponse(new URL(URL_FIRST, "script.js"), "", "text/javascript");
982         client.setWebConnection(webConnection);
983 
984         final HtmlPage page = client.getPage(URL_FIRST);
985 
986         String xml = page.asXml();
987         final String prefix = "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>";
988         assertTrue(xml.startsWith(prefix));
989         xml = xml.substring(prefix.length());
990         assertEquals(html, xml
991                 .replaceAll("[\\n\\r]", "")
992                 .replaceAll("\\s\\s+", "")
993                 .replaceAll("\"", "'"));
994     }
995 
996     /**
997      * @exception Exception If the test fails
998      */
999     @Test
1000     public void asXml2() throws Exception {
1001         final String htmlContent = DOCTYPE_HTML
1002             + "<html><head><title>foo</title>\n"
1003             + "<script>var test = 15 < 16;</script></head>\n"
1004             + "</head>\n"
1005             + "<body onload='test=(1 > 2) && (45 < 78)'><p>helloworld &amp;amp; helloall</p></body>\n"
1006             + "</html>";
1007 
1008         final HtmlPage page = loadPage(htmlContent);
1009         assertNotNull("xml document could not be parsed", page.asXml());
1010         final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
1011         final DocumentBuilder builder = factory.newDocumentBuilder();
1012         builder.parse(IOUtils.toInputStream(page.asXml(), StandardCharsets.ISO_8859_1));
1013     }
1014 
1015     /**
1016      * @throws Exception if the test fails
1017      */
1018     @Test
1019     public void asXml_unicode() throws Exception {
1020         final String unicodeString = "\u064A\u0627 \u0644\u064A\u064A\u0644";
1021         final String html = DOCTYPE_HTML
1022             + "<html>\n"
1023             + "<head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8'></head>\n"
1024             + "<body><span id='foo'>" + unicodeString + "</span></body></html>";
1025 
1026         final WebClient client = getWebClient();
1027         final MockWebConnection webConnection = new MockWebConnection();
1028 
1029         webConnection.setDefaultResponse(StringUtils.toByteArray(html, UTF_8), 200, "OK", MimeType.TEXT_HTML);
1030         client.setWebConnection(webConnection);
1031 
1032         final HtmlPage page = client.getPage(URL_FIRST);
1033         final String xml = page.asXml();
1034         assertTrue(xml.contains("<?xml "));
1035         assertTrue(xml.contains(unicodeString));
1036     }
1037 
1038     /**
1039      * @throws Exception if the test fails
1040      */
1041     @Test
1042     public void asXml_noscript() throws Exception {
1043         final String html = DOCTYPE_HTML
1044             + "<html>"
1045             + "<body>"
1046             + "<noscript><p><strong>your browser does not support JavaScript</strong></p></noscript>"
1047             + "</body></html>";
1048 
1049         final String expected = "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\r\n"
1050                 + "<html>\r\n"
1051                 + "  <head/>\r\n"
1052                 + "  <body>\r\n"
1053                 + "    <noscript>"
1054                             + "&lt;p&gt;&lt;strong&gt;your browser does not support JavaScript&lt;/strong&gt;&lt;/p&gt;"
1055                             + "</noscript>\r\n"
1056                 + "  </body>\r\n"
1057                 + "</html>";
1058 
1059         final HtmlPage page = loadPage(html);
1060         assertEquals(expected, page.asXml());
1061     }
1062 
1063     /**
1064      * @exception Exception if the test fails
1065      */
1066     @Test
1067     public void getElementsById() throws Exception {
1068         final String html = DOCTYPE_HTML
1069                 + "<html><body>"
1070                 + "<div id='a'>foo</div>"
1071                 + "<div id='b'/><div id='b'/>"
1072                 + "<div id='c'><div id='c'/></div>"
1073                 + "</body></html>";
1074         final HtmlPage page = loadPage(html);
1075         assertEquals(1, page.getElementsById("a").size());
1076         assertEquals(2, page.getElementsById("b").size());
1077         assertEquals(2, page.getElementsById("c").size());
1078         assertEquals(0, page.getElementsById("d").size());
1079 
1080         final DomElement a = page.getElementsById("a").get(0);
1081         a.remove();
1082         assertEquals(0, page.getElementsById("a").size());
1083 
1084         final DomElement b1 = page.getElementsById("b").get(0);
1085         b1.appendChild(a);
1086 
1087         assertEquals(1, page.getElementsById("a").size());
1088     }
1089 
1090     /**
1091      * @exception Exception if the test fails
1092      */
1093     @Test
1094     public void getElementsByName() throws Exception {
1095         final String html = DOCTYPE_HTML
1096                 + "<html><body><div name='a'>foo</div><div name='b'/><div name='b'/></body></html>";
1097         final HtmlPage page = loadPage(html);
1098         assertEquals(1, page.getElementsByName("a").size());
1099         assertEquals(2, page.getElementsByName("b").size());
1100         assertEquals(0, page.getElementsByName("c").size());
1101 
1102         final DomElement a = page.getElementsByName("a").get(0);
1103         a.remove();
1104         assertEquals(0, page.getElementsByName("a").size());
1105 
1106         final DomElement b1 = page.getElementsByName("b").get(0);
1107         b1.appendChild(a);
1108         assertEquals(1, page.getElementsByName("a").size());
1109     }
1110 
1111     /**
1112      * @exception Exception if the test fails
1113      */
1114     @Test
1115     public void getElementByName() throws Exception {
1116         final String html = DOCTYPE_HTML
1117             + "<html><body>\n"
1118             + "<div id='a' name='a'>foo</div>\n"
1119             + "<div id='b1' name='b'>bar</div>\n"
1120             + "<div id='b2' name='b'>baz</div></body></html>";
1121         final HtmlPage page = loadPage(html);
1122         assertEquals(page.getElementById("a"), page.getElementByName("a"));
1123         assertEquals(page.getElementById("b1"), page.getElementByName("b"));
1124 
1125         page.getElementByName("b").remove();
1126         assertEquals(page.getElementById("b2"), page.getElementByName("b"));
1127     }
1128 
1129     /**
1130      * @exception Exception if the test fails
1131      */
1132     @Test
1133     public void getElementByNameNotfound() throws Exception {
1134         final String html = DOCTYPE_HTML
1135             + "<html><body>\n"
1136             + "<div id='a' name='a'>foo</div>\n"
1137             + "<div id='b1' name='b'>bar</div>\n"
1138             + "<div id='b2' name='b'>baz</div></body></html>";
1139         final HtmlPage page = loadPage(html);
1140 
1141         Assertions.assertThrows(ElementNotFoundException.class, () -> page.getElementByName("c"));
1142     }
1143 
1144     /**
1145      * @exception Exception if the test fails
1146      */
1147     @Test
1148     public void getHtmlElementsByIdAndOrName() throws Exception {
1149         final String html = DOCTYPE_HTML
1150                 + "<html><body><div name='a' id='a'>foo</div><div name='b' id='c'>bar</div>"
1151                 + "<div name='b' id='d'>bar</div></body></html>";
1152         final HtmlPage page = loadPage(html);
1153         assertEquals(1, page.getElementsByIdAndOrName("a").size());
1154         assertEquals(2, page.getElementsByIdAndOrName("b").size());
1155         assertEquals(1, page.getElementsByIdAndOrName("c").size());
1156         assertEquals(1, page.getElementsByIdAndOrName("d").size());
1157 
1158         final DomElement a = page.getElementsByIdAndOrName("a").get(0);
1159         a.remove();
1160         assertEquals(0, page.getElementsByIdAndOrName("a").size());
1161 
1162         final DomElement b1 = page.getElementsByIdAndOrName("b").get(0);
1163         b1.appendChild(a);
1164         assertEquals(1, page.getElementsByIdAndOrName("a").size());
1165     }
1166 
1167     /**
1168      * Regression test for bug #287.
1169      * @exception Exception If the test fails
1170      */
1171     @Test
1172     public void getHtmlElementByIdAfterRemove() throws Exception {
1173         final String htmlContent = DOCTYPE_HTML
1174             + "<html><head><title>foo</title></head>\n"
1175             + "<body>\n"
1176             + "<div id='div1'>\n"
1177             + "<div id='div2'>\n"
1178             + "</div>\n"
1179             + "</div>\n"
1180             + "</body>\n"
1181             + "</html>";
1182 
1183         final HtmlPage page = loadPage(htmlContent);
1184         final HtmlElement div1 = page.getHtmlElementById("div1");
1185         page.getHtmlElementById("div2"); // would throw if not found
1186         div1.remove();
1187         try {
1188             page.getHtmlElementById("div1"); // throws if not found
1189             Assertions.fail("div1 should have been removed");
1190         }
1191         catch (final ElementNotFoundException e) {
1192             // nothing
1193         }
1194 
1195         try {
1196             page.getHtmlElementById("div2"); // throws if not found
1197             Assertions.fail("div2 should have been removed");
1198         }
1199         catch (final ElementNotFoundException e) {
1200             // nothing
1201         }
1202     }
1203 
1204     /**
1205      * Test getHtmlElementById() when 2 elements have the same id and the first one
1206      * is removed.
1207      * @exception Exception If the test fails
1208      */
1209     @Test
1210     public void getHtmlElementById_idTwice() throws Exception {
1211         final String htmlContent = DOCTYPE_HTML
1212             + "<html><head><title>foo</title></head>\n"
1213             + "<body>\n"
1214             + "<div id='id1'>foo</div>\n"
1215             + "<span id='id1'>bla</span>\n"
1216             + "</body>\n"
1217             + "</html>";
1218 
1219         final HtmlPage page = loadPage(htmlContent);
1220         final HtmlElement elt1 = page.getHtmlElementById("id1");
1221         assertEquals("div", elt1.getNodeName());
1222         elt1.remove();
1223         assertEquals("span", page.getHtmlElementById("id1").getNodeName());
1224     }
1225 
1226     /**
1227      * Test the "set-cookie" meta tag.
1228      * @throws Exception if the test fails
1229      */
1230     @Test
1231     @Alerts("webm=none")
1232     public void setCookieMetaTag() throws Exception {
1233         final String content = DOCTYPE_HTML
1234             + "<html><head>\n"
1235             + "<meta http-equiv='set-cookie' content='webm=none; path=/;'>\n"
1236             + "</head><body>\n"
1237             + "<script>document.title = document.cookie</script>\n"
1238             + "</body></html>";
1239 
1240         final HtmlPage page = loadPage(content);
1241         assertEquals(getExpectedAlerts()[0], page.getTitleText());
1242 
1243         final Set<Cookie> cookies = page.getWebClient().getCookieManager().getCookies();
1244         assertEquals(1, cookies.size());
1245         final Cookie cookie = cookies.iterator().next();
1246         assertEquals(page.getUrl().getHost(), cookie.getDomain());
1247         assertEquals("webm", cookie.getName());
1248         assertEquals("none", cookie.getValue());
1249         assertEquals("/", cookie.getPath());
1250     }
1251 
1252     /**
1253      * Test for bug #474.
1254      * @throws Exception if the test fails
1255      */
1256     @Test
1257     public void noSlashURL() throws Exception {
1258         testNoSlashURL("http:/second");
1259         testNoSlashURL("http:second");
1260     }
1261 
1262     private void testNoSlashURL(final String url) throws Exception {
1263         final String firstContent = DOCTYPE_HTML
1264             + "<html><body>\n"
1265             + "<iframe id='myIFrame' src='" + url + "'></iframe>\n"
1266             + "</body></html>";
1267 
1268         final String secondContent = DOCTYPE_HTML + "<html><body></body></html>";
1269         final WebClient client = getWebClient();
1270 
1271         final URL secondURL = new URL("http://second/");
1272         final MockWebConnection webConnection = new MockWebConnection();
1273         webConnection.setResponse(URL_FIRST, firstContent);
1274         webConnection.setResponse(secondURL, secondContent);
1275 
1276         client.setWebConnection(webConnection);
1277 
1278         final HtmlPage firstPage = client.getPage(URL_FIRST);
1279         final HtmlInlineFrame iframe = firstPage.getHtmlElementById("myIFrame");
1280 
1281         assertEquals(secondURL, iframe.getEnclosedPage().getUrl());
1282     }
1283 
1284     /**
1285      * @throws Exception failure
1286      */
1287     @Test
1288     public void metaTagWithEmptyURL() throws Exception {
1289         final WebClient client = getWebClient();
1290         client.setRefreshHandler(new ImmediateRefreshHandler());
1291 
1292         // connection will return a page with <meta ... refresh> for the first call
1293         // and the same page without it for the other calls
1294         final MockWebConnection webConnection = new MockWebConnection() {
1295             private int nbCalls_ = 0;
1296             @Override
1297             public WebResponse getResponse(final WebRequest request) throws IOException {
1298                 String content = DOCTYPE_HTML + "<html><head>\n";
1299                 if (nbCalls_ == 0) {
1300                     content += "<meta http-equiv='refresh' content='1; URL='>\n";
1301                 }
1302                 content += "</head><body></body></html>";
1303                 nbCalls_++;
1304 
1305                 final StringWebResponse response = new StringWebResponse(content, request.getUrl());
1306                 response.getWebRequest().setHttpMethod(request.getHttpMethod());
1307                 return response;
1308             }
1309         };
1310         client.setWebConnection(webConnection);
1311 
1312         final WebRequest request = new WebRequest(URL_FIRST);
1313         request.setHttpMethod(HttpMethod.POST);
1314         client.getPage(request);
1315     }
1316 
1317     /**
1318      * @throws Exception if the test fails
1319      */
1320     @Test
1321     public void serialization() throws Exception {
1322         // The document.all and form.elements calls are important because they trigger the creation
1323         // of HTMLCollections, which have caused serialization problems in the past (see bug #606).
1324 
1325         final String content = DOCTYPE_HTML
1326             + "<html><body>\n"
1327             + "<div id='myId'>Hello there!</div>\n"
1328             + "<script>\n"
1329             + "  var x = document.all;\n"
1330             + "  window.onload = function() {alert('foo')};\n"
1331 
1332             // this tests 3103703
1333             // we don't store the jobs are pending at the moment of serialization
1334             + "  var aktiv = window.setInterval('foo()', 1000);\n"
1335             + "  var i = 0;\n"
1336             + "  function foo() {\n"
1337             + "    i = i + 1;\n"
1338             + "    if (i >= 10)\n"
1339             + "      window.clearInterval(aktiv);\n"
1340             + "  }\n"
1341             + "</script>\n"
1342             + "<form name='f' id='f'></form>\n"
1343             + "<script>var y = document.getElementById('f').elements;</script>\n"
1344             + "</body></html>";
1345 
1346         // waiting for the alerts creates some more js objects associated with the page
1347         // this tests 3103703
1348         final List<String> expectedAlerts = new LinkedList<>();
1349         expectedAlerts.add("foo");
1350 
1351         final HtmlPage page1 = loadPage(content, expectedAlerts);
1352         final byte[] bytes = SerializationUtils.serialize(page1);
1353 
1354         final HtmlPage page2 = (HtmlPage) SerializationUtils.deserialize(bytes);
1355 
1356         final Iterator<HtmlElement> iterator1 = page1.getHtmlElementDescendants().iterator();
1357         final Iterator<HtmlElement> iterator2 = page2.getHtmlElementDescendants().iterator();
1358         while (iterator1.hasNext()) {
1359             assertTrue(iterator2.hasNext());
1360             final HtmlElement element1 = iterator1.next();
1361             final HtmlElement element2 = iterator2.next();
1362             assertEquals(element1.getNodeName(), element2.getNodeName());
1363         }
1364         assertFalse(iterator2.hasNext());
1365         assertEquals("Hello there!", page2.getHtmlElementById("myId").getFirstChild().getNodeValue());
1366     }
1367 
1368     /**
1369      * Test issue 513.
1370      * @throws Exception if the test fails
1371      */
1372     @Test
1373     public void serializationLambda() throws Exception {
1374         final String content = DOCTYPE_HTML
1375             + "<html><body>\n"
1376             + "<div id='myId'>Hello there!</div>\n"
1377             + "<form name='f' id='f'></form>\n"
1378             + "<script>var y = document.body.getElementsByTagName('form').elements;</script>\n"
1379             + "</body></html>";
1380 
1381         final HtmlPage page1 = loadPage(content);
1382         final byte[] bytes = SerializationUtils.serialize(page1);
1383 
1384         final HtmlPage page2 = (HtmlPage) SerializationUtils.deserialize(bytes);
1385 
1386         final Iterator<HtmlElement> iterator1 = page1.getHtmlElementDescendants().iterator();
1387         final Iterator<HtmlElement> iterator2 = page2.getHtmlElementDescendants().iterator();
1388         while (iterator1.hasNext()) {
1389             assertTrue(iterator2.hasNext());
1390             final HtmlElement element1 = iterator1.next();
1391             final HtmlElement element2 = iterator2.next();
1392             assertEquals(element1.getNodeName(), element2.getNodeName());
1393         }
1394         assertFalse(iterator2.hasNext());
1395         assertEquals("Hello there!", page2.getHtmlElementById("myId").getFirstChild().getNodeValue());
1396     }
1397 
1398     /**
1399      * @throws Exception if the test fails
1400      */
1401     @Test
1402     public void serializationStaticDomNodeList() throws Exception {
1403         final String content = DOCTYPE_HTML
1404             + "<html><body>\n"
1405             + "<div id='myId'>Hello there!</div>\n"
1406             + "<form name='f' id='f'></form>\n"
1407             + "<script>var y = document.querySelectorAll('*');</script>\n"
1408             + "</body></html>";
1409 
1410         final HtmlPage page1 = loadPage(content);
1411         final byte[] bytes = SerializationUtils.serialize(page1);
1412 
1413         final HtmlPage page2 = (HtmlPage) SerializationUtils.deserialize(bytes);
1414 
1415         final Iterator<HtmlElement> iterator1 = page1.getHtmlElementDescendants().iterator();
1416         final Iterator<HtmlElement> iterator2 = page2.getHtmlElementDescendants().iterator();
1417         while (iterator1.hasNext()) {
1418             assertTrue(iterator2.hasNext());
1419             final HtmlElement element1 = iterator1.next();
1420             final HtmlElement element2 = iterator2.next();
1421             assertEquals(element1.getNodeName(), element2.getNodeName());
1422         }
1423         assertFalse(iterator2.hasNext());
1424         assertEquals("Hello there!", page2.getHtmlElementById("myId").getFirstChild().getNodeValue());
1425     }
1426 
1427     /**
1428      * Verifies that a cloned HtmlPage has its own "idMap_".
1429      * @throws Exception if the test fails
1430      */
1431     @Test
1432     public void clonedPageHasOwnIdMap() throws Exception {
1433         final String content = DOCTYPE_HTML
1434             + "<html><head><title>foo</title>"
1435             + "<body>"
1436             + "<div id='id1' class='cl1'><div id='id2' class='cl2'></div></div>"
1437             + "</body></html>";
1438 
1439         final HtmlPage page = loadPage(content);
1440         final HtmlElement id1 = (HtmlElement) page.getDocumentElement().getLastChild().getLastChild();
1441         assertEquals("id1", id1.getId());
1442         assertSame(id1, page.getHtmlElementById("id1"));
1443         final HtmlPage clone = page.cloneNode(true);
1444         assertSame(id1, page.getHtmlElementById("id1"));
1445         final HtmlElement id1clone = (HtmlElement) clone.getDocumentElement().getLastChild().getLastChild();
1446         assertNotSame(id1, id1clone);
1447         assertEquals("id1", id1clone.getId());
1448         assertSame(id1clone, clone.getHtmlElementById("id1"));
1449         assertNotSame(id1clone, page.getHtmlElementById("id1"));
1450         assertNotSame(id1, clone.getHtmlElementById("id1"));
1451 
1452         page.getHtmlElementById("id2").remove();
1453         try {
1454             page.getHtmlElementById("id2");
1455             Assertions.fail("should have thrown ElementNotFoundException");
1456         }
1457         catch (final ElementNotFoundException expected) {
1458         }
1459         assertNotNull(clone.getHtmlElementById("id2"));
1460     }
1461 
1462     /**
1463      * Verifies that a cloned HtmlPage has its own "documentElement".
1464      * @throws Exception if the test fails
1465      */
1466     @Test
1467     public void clonedPageHasOwnDocumentElement() throws Exception {
1468         final String content = DOCTYPE_HTML
1469             + "<html><head><title>foo</title>\n"
1470             + "<body>\n"
1471             + "<div id='id1' class='cl1'><div id='id2' class='cl2'></div></div>\n"
1472             + "</body></html>";
1473 
1474         final HtmlPage page = loadPage(content);
1475         final HtmlPage clone = page.cloneNode(true);
1476         assertTrue(page != clone);
1477         final DomElement doc = page.getDocumentElement();
1478         final DomElement docclone = clone.getDocumentElement();
1479         assertTrue(doc != docclone);
1480     }
1481 
1482     /**
1483      * @throws Exception if the test fails
1484      */
1485     @Test
1486     public void htmlAttributeChangeListener_AddAttribute() throws Exception {
1487         final String htmlContent = DOCTYPE_HTML
1488             + "<html><head><title>foo</title>\n"
1489             + "<script>\n"
1490             + "  function clickMe() {\n"
1491             + "    var p1 = document.getElementById('p1');\n"
1492             + "    p1.setAttribute('title', 'myTitle');\n"
1493             + "  }\n"
1494             + "</script>\n"
1495             + "</head>\n"
1496             + "<body>\n"
1497             + "<p id='p1'></p>\n"
1498             + "<input id='myButton' type='button' onclick='clickMe()'>\n"
1499             + "</body></html>";
1500 
1501         final String[] expectedValues = {"attributeAdded: p,title,myTitle"};
1502         final HtmlPage page = loadPage(htmlContent);
1503         final HtmlAttributeChangeListenerTestImpl listenerImpl = new HtmlAttributeChangeListenerTestImpl();
1504         page.addHtmlAttributeChangeListener(listenerImpl);
1505         final HtmlButtonInput myButton = page.getHtmlElementById("myButton");
1506 
1507         myButton.click();
1508         assertEquals(expectedValues, listenerImpl.getCollectedValues());
1509     }
1510 
1511     /**
1512      * @throws Exception if the test fails
1513      */
1514     @Test
1515     public void htmlAttributeChangeListener_ReplaceAttribute() throws Exception {
1516         final String htmlContent = DOCTYPE_HTML
1517             + "<html><head><title>foo</title>\n"
1518             + "<script>\n"
1519             + "  function clickMe() {\n"
1520             + "    var p1 = document.getElementById('p1');\n"
1521             + "    p1.setAttribute('title', p1.getAttribute('title') + 'a');\n"
1522             + "  }\n"
1523             + "</script>\n"
1524             + "</head>\n"
1525             + "<body>\n"
1526             + "<p id='p1' title='myTitle'></p>\n"
1527             + "<input id='myButton' type='button' onclick='clickMe()'>\n"
1528             + "</body></html>";
1529 
1530         final String[] expectedValues = {"attributeReplaced: p,title,myTitle"};
1531         final HtmlPage page = loadPage(htmlContent);
1532         final HtmlAttributeChangeListenerTestImpl listenerImpl = new HtmlAttributeChangeListenerTestImpl();
1533         page.addHtmlAttributeChangeListener(listenerImpl);
1534         final HtmlButtonInput myButton = page.getHtmlElementById("myButton");
1535 
1536         myButton.click();
1537         assertEquals(expectedValues, listenerImpl.getCollectedValues());
1538     }
1539 
1540     /**
1541      * @throws Exception if the test fails
1542      */
1543     @Test
1544     public void htmlAttributeChangeListener_RemoveAttribute() throws Exception {
1545         final String htmlContent = DOCTYPE_HTML
1546             + "<html><head><title>foo</title>\n"
1547             + "<script>\n"
1548             + "  function clickMe() {\n"
1549             + "    var p1 = document.getElementById('p1');\n"
1550             + "    p1.removeAttribute('title');\n"
1551             + "  }\n"
1552             + "</script>\n"
1553             + "</head>\n"
1554             + "<body>\n"
1555             + "<p id='p1' title='myTitle'></p>\n"
1556             + "<input id='myButton' type='button' onclick='clickMe()'>\n"
1557             + "</body></html>";
1558 
1559         final String[] expectedValues = {"attributeRemoved: p,title,myTitle"};
1560         final HtmlPage page = loadPage(htmlContent);
1561         final HtmlAttributeChangeListenerTestImpl listenerImpl = new HtmlAttributeChangeListenerTestImpl();
1562         page.addHtmlAttributeChangeListener(listenerImpl);
1563         final HtmlButtonInput myButton = page.getHtmlElementById("myButton");
1564 
1565         myButton.click();
1566         assertEquals(expectedValues, listenerImpl.getCollectedValues());
1567     }
1568 
1569     /**
1570      * @throws Exception if the test fails
1571      */
1572     @Test
1573     public void htmlAttributeChangeListener_RemoveListener() throws Exception {
1574         final String htmlContent = DOCTYPE_HTML
1575             + "<html><head><title>foo</title>\n"
1576             + "<script>\n"
1577             + "  function clickMe() {\n"
1578             + "    var p1 = document.getElementById('p1');\n"
1579             + "    p1.setAttribute('title', p1.getAttribute('title') + 'a');\n"
1580             + "  }\n"
1581             + "</script>\n"
1582             + "</head>\n"
1583             + "<body>\n"
1584             + "<p id='p1' title='myTitle'></p>\n"
1585             + "<input id='myButton' type='button' onclick='clickMe()'>\n"
1586             + "</body></html>";
1587 
1588         final String[] expectedValues = {"attributeReplaced: p,title,myTitle"};
1589         final HtmlPage page = loadPage(htmlContent);
1590         final HtmlAttributeChangeListenerTestImpl listenerImpl = new HtmlAttributeChangeListenerTestImpl();
1591         page.addHtmlAttributeChangeListener(listenerImpl);
1592         final HtmlButtonInput myButton = page.getHtmlElementById("myButton");
1593 
1594         myButton.click();
1595         page.removeHtmlAttributeChangeListener(listenerImpl);
1596         myButton.click();
1597         assertEquals(expectedValues, listenerImpl.getCollectedValues());
1598     }
1599 
1600     /**
1601      * @throws Exception if the test fails
1602      */
1603     @Test
1604     public void htmlAttributeChangeListener_ListenerRegistersNewListener() throws Exception {
1605         final String htmlContent = DOCTYPE_HTML
1606             + "<html><head><title>foo</title>\n"
1607             + "</head>\n"
1608             + "<body>\n"
1609             + "<p id='p1' title='myTitle'></p>\n"
1610             + "</body></html>";
1611 
1612         final HtmlPage page = loadPage(htmlContent);
1613 
1614         final List<String> collector = new ArrayList<>();
1615         final HtmlAttributeChangeListener listener2 = new HtmlAttributeChangeListenerTestImpl() {
1616             @Override
1617             public void attributeReplaced(final HtmlAttributeChangeEvent event) {
1618                 collector.add("in listener 2");
1619             }
1620         };
1621         final HtmlAttributeChangeListener listener1 = new HtmlAttributeChangeListenerTestImpl() {
1622             @Override
1623             public void attributeReplaced(final HtmlAttributeChangeEvent event) {
1624                 collector.add("in listener 1");
1625                 page.addHtmlAttributeChangeListener(listener2);
1626             }
1627         };
1628 
1629         page.addHtmlAttributeChangeListener(listener1);
1630 
1631         final HtmlElement p = page.getHtmlElementById("p1");
1632         p.setAttribute("title", "new title");
1633 
1634         final String[] expectedValues = {"in listener 1"};
1635         assertEquals(expectedValues, collector);
1636         collector.clear();
1637 
1638         p.setAttribute("title", "new new title");
1639         final String[] expectedValues2 = {"in listener 1", "in listener 2"};
1640         assertEquals(expectedValues2, collector);
1641     }
1642 
1643     /**
1644      * @throws Exception if the test fails
1645      */
1646     @Test
1647     public void caseInsensitiveRegexReplacement() throws Exception {
1648         final String html = DOCTYPE_HTML
1649             + "<html><body><script>\n"
1650             + "var r = /^([#.]?)([a-z0-9\\*_-]*)/i;\n"
1651             + "var s = '#userAgent';\n"
1652             + "s = s.replace(r, '');\n"
1653             + "alert(s.length);\n"
1654             + "</script></body></html>";
1655         final String[] expectedAlerts = {"0"};
1656         final List<String> actualAlerts = new ArrayList<>();
1657         loadPage(html, actualAlerts);
1658         assertEquals(expectedAlerts, actualAlerts);
1659     }
1660 
1661     /**
1662      * @throws Exception if the test fails
1663      */
1664     @Test
1665     public void regexReplacementWithFunction() throws Exception {
1666         final String html = DOCTYPE_HTML
1667             + "<html><body><script>\n"
1668             + "var r = /-([a-z])/ig;\n"
1669             + "var s = 'font-size';\n"
1670             + "s = s.replace(r, function(z,b){return b.toUpperCase();});\n"
1671             + "alert(s);\n"
1672             + "</script></body></html>";
1673         final String[] expectedAlerts = {"fontSize"};
1674         final List<String> collectedAlerts = new ArrayList<>();
1675         loadPage(html, collectedAlerts);
1676         assertEquals(expectedAlerts, collectedAlerts);
1677     }
1678 
1679     /**
1680      * @throws Exception if the test fails
1681      */
1682     @Test
1683     public void title_EmptyXmlTagExpanded() throws Exception {
1684         final String content = DOCTYPE_HTML
1685             + "<html><head><title/></head>\n"
1686             + "<body>Hello World!</body></html>";
1687         final HtmlPage page = loadPage(content);
1688         assertTrue(page.asXml().indexOf("</title>") != -1);
1689     }
1690 
1691     /**
1692      * @throws Exception if the test fails
1693      */
1694     @Test
1695     public void onunLoadHandler() throws Exception {
1696         final String htmlContent = DOCTYPE_HTML
1697             + "<html><head><title>foo</title>\n"
1698             + "</head><body onunload='alert(\"foo\");alert(\"bar\")'>\n"
1699             + "</body></html>";
1700 
1701         final String[] expectedAlerts = {"foo", "bar"};
1702         final List<String> collectedAlerts = new ArrayList<>();
1703         final WebClient client = getWebClient();
1704         client.setAlertHandler(new CollectingAlertHandler(collectedAlerts));
1705         final MockWebConnection conn = new MockWebConnection();
1706         conn.setResponse(URL_FIRST, htmlContent);
1707         client.setWebConnection(conn);
1708 
1709         client.getPage(URL_FIRST);
1710         client.getPage(URL_FIRST);
1711 
1712         assertEquals(expectedAlerts, collectedAlerts);
1713     }
1714 
1715     /**
1716      * @throws Exception if the test fails
1717      */
1718     @Test
1719     public void onbeforeunloadHandler_ok() throws Exception {
1720         testOnbeforeunloadHandler(true, "second");
1721     }
1722 
1723     /**
1724      * @throws Exception if the test fails
1725      */
1726     @Test
1727     public void onbeforeunloadHandler_cancel() throws Exception {
1728         testOnbeforeunloadHandler(false, "first");
1729     }
1730 
1731     /**
1732      * @param browserVersion the browser version to use
1733      * @param handlerOk whether <tt>OnbeforeunloadHandler.handleEvent</tt> will return {@code true} of {@code false}
1734      * @param expectedPageTitle the expected title of the page after clicking
1735      */
1736     private void testOnbeforeunloadHandler(final boolean handlerOk, final String expectedPageTitle) throws Exception {
1737         final WebClient webClient = getWebClient();
1738         final MockWebConnection webConnection = new MockWebConnection();
1739         final List<String> collectedConfirms = new ArrayList<>();
1740 
1741         webClient.setOnbeforeunloadHandler(new OnbeforeunloadHandler() {
1742             @Override
1743             public boolean handleEvent(final Page page, final String message) {
1744                 collectedConfirms.add(message);
1745                 return handlerOk;
1746             }
1747         });
1748 
1749         final String expectedMessage = "Any string value here forces a dialog box to appear before closing the window.";
1750         final String firstContent = DOCTYPE_HTML
1751             + "<html><head><title>first</title>\n"
1752             + "<script>\n"
1753             + "  function closeIt(event) {\n"
1754             + "    event.returnValue = '" + expectedMessage + "';\n"
1755             + "  }\n"
1756             + "</script>\n"
1757             + "</head><body onbeforeunload='closeIt(event)'>\n"
1758             + "  <a href='" + URL_SECOND + "'>Second page</a>\n"
1759             + "</body></html>";
1760 
1761         final String secondContent = DOCTYPE_HTML
1762             + "<html><head><title>second</title>\n"
1763             + "</head><body>\n"
1764             + "</body></html>";
1765 
1766         webConnection.setResponse(URL_FIRST, firstContent);
1767         webConnection.setResponse(URL_SECOND, secondContent);
1768         webClient.setWebConnection(webConnection);
1769 
1770         final HtmlPage page = webClient.getPage(URL_FIRST);
1771         final HtmlAnchor anchor = page.getAnchors().get(0);
1772         final HtmlPage secondPage = anchor.click();
1773 
1774         assertEquals(new String[] {expectedMessage}, collectedConfirms);
1775         assertEquals(expectedPageTitle, secondPage.getTitleText());
1776     }
1777 
1778     /**
1779      * @throws Exception if an error occurs
1780      */
1781     @Test
1782     public void srcJavaScript() throws Exception {
1783         final String htmlContent = DOCTYPE_HTML
1784             + "<html><head><title>foo</title></head><body>\n"
1785             + "<script id='ie_ready' src='javascript:void(0)'></script>\n"
1786             + "</body></html>";
1787 
1788         loadPage(htmlContent);
1789     }
1790 
1791     /**
1792      * Regression test for asNormalizedText() which would blow up.
1793      *
1794      * @exception Exception If the test fails
1795      */
1796     @Test
1797     public void asNormalizedText() throws Exception {
1798         final String htmlContent = DOCTYPE_HTML
1799             + "<html><head><title>test</title></head>\n"
1800             + "<body><table>\n"
1801             + "<tr><form><td>a</td></form></tr>\n"
1802             + "</table></body></html>";
1803 
1804         final HtmlPage page = loadPage(htmlContent);
1805         assertEquals("test\na", page.asNormalizedText());
1806     }
1807 
1808     /**
1809      * @throws Exception if the test fails
1810      */
1811     @Test
1812     public void getElementsByTagName() throws Exception {
1813         final String firstContent = DOCTYPE_HTML
1814             + "<html><head><title>First</title></head>\n"
1815             + "<body>\n"
1816             + "<form><input type='button' name='button1' value='pushme'></form>\n"
1817             + "<div>a</div> <div>b</div> <div>c</div>\n"
1818             + "</body></html>";
1819 
1820         final HtmlPage page = loadPage(firstContent);
1821 
1822         NodeList inputs = page.getElementsByTagName("input");
1823         assertEquals(1, inputs.getLength());
1824         assertEquals("button", inputs.item(0).getAttributes().getNamedItem("type").getNodeValue());
1825 
1826         final NodeList divs = page.getElementsByTagName("div");
1827         assertEquals(3, divs.getLength());
1828 
1829         final HtmlDivision newDiv = new HtmlDivision(HtmlDivision.TAG_NAME, page, null);
1830         page.getBody().appendChild(newDiv);
1831         assertEquals(4, divs.getLength());
1832 
1833         // case sensitive
1834         inputs = page.getElementsByTagName("inPUT");
1835         assertEquals(1, inputs.getLength());
1836 
1837         // empty
1838         inputs = page.getElementsByTagName("");
1839         assertEquals(0, inputs.getLength());
1840 
1841         // null
1842         inputs = page.getElementsByTagName(null);
1843         assertEquals(0, inputs.getLength());
1844     }
1845 
1846     /**
1847      * HtmlPage.getReadyState() should give the same information than the document element.
1848      * @see <a href="http://sourceforge.net/p/htmlunit/bugs/402/">402</a>
1849      * @exception Exception If the test fails
1850      */
1851     @Test
1852     public void readyState() throws Exception {
1853         final String htmlContent = DOCTYPE_HTML
1854             + "<html><head><title>test</title></head>\n"
1855             + "<body><table>\n"
1856             + "<tr><form><td>a</td></form></tr>\n"
1857             + "</table></body></html>";
1858 
1859         final HtmlPage page = loadPage(htmlContent);
1860         assertEquals(DomNode.READY_STATE_COMPLETE, page.getReadyState());
1861     }
1862 
1863     /**
1864      * @exception Exception If the test fails
1865      */
1866     @Test
1867     public void cloneNode() throws Exception {
1868         final String html = DOCTYPE_HTML
1869             + "<html>\n"
1870             + "<head><title>foo</title></head>\n"
1871             + "<body>\n"
1872             + "<p>hello world</p>\n"
1873             + "</body></html>";
1874         final HtmlPage page = loadPage(html);
1875         page.getByXPath("//p");
1876         final HtmlPage clonedPage = page.cloneNode(true);
1877         clonedPage.getByXPath("//p");
1878     }
1879 
1880     /**
1881      * @exception Exception If the test fails
1882      */
1883     @Test
1884     public void cloneHtmlPageWithFrame() throws Exception {
1885         final String html = DOCTYPE_HTML
1886                 + "<html>\n"
1887                 + "<head></head><body>\n"
1888                 + "<div id='content'>"
1889                 + "  <iframe name='content' src='frame1.html'></iframe>"
1890                 + "</div>"
1891                 + "</body></html>";
1892 
1893         final String frameContent = DOCTYPE_HTML
1894                 + "<html>\n"
1895                 + "<head></head>\n"
1896                 + "<body>"
1897                 + "  <p>frame1</p>"
1898                 + "</body></html>";
1899 
1900         final MockWebConnection webConnection = getMockWebConnection();
1901         webConnection.setResponse(new URL("http://example/index.html"), html);
1902         webConnection.setResponse(new URL("http://example/frame1.html"), frameContent);
1903 
1904         final WebClient webClient = getWebClientWithMockWebConnection();
1905 
1906         final HtmlPage page = webClient.getPage("http://example/index.html");
1907 
1908         // check frame on page
1909         final List<FrameWindow> frames = page.getFrames();
1910         assertEquals(1, frames.size());
1911         assertEquals("frame1", ((HtmlPage) frames.get(0).getEnclosedPage()).asNormalizedText());
1912 
1913         // clone page with deep false
1914         HtmlPage clonedPage = page.cloneNode(false);
1915 
1916         assertEquals(1, page.getFrames().size());
1917         assertEquals(1, clonedPage.getFrames().size());
1918 
1919         // clone page with deep true
1920         clonedPage = page.cloneNode(true);
1921 
1922         // must be equals 1
1923         assertEquals(1, page.getFrames().size());
1924         assertEquals(1, clonedPage.getFrames().size());
1925 
1926         // clone page with deep true
1927         page.executeJavaScript("document.cloneNode(true)");
1928 
1929         // must be equals 1
1930         assertEquals(1, page.getFrames().size());
1931         assertEquals(1, clonedPage.getFrames().size());
1932     }
1933 
1934     /**
1935      * @exception Exception If the test fails
1936      */
1937     @Test
1938     public void addAutoCloseable() throws Exception {
1939         final String html = "";
1940         final HtmlPage page = loadPage(html);
1941         page.addAutoCloseable(new AutoCloseable() {
1942             @Override
1943             public void close() throws Exception {
1944                 page.addAutoCloseable(new AutoCloseable() {
1945                     @Override
1946                     public void close() throws Exception {
1947                         throw new NullPointerException("close failed");
1948                     }
1949                 });
1950             }
1951         });
1952         page.cleanUp();
1953     }
1954 
1955     /**
1956      * @exception Exception If the test fails
1957      */
1958     @Test
1959     public void addAutoCloseableNull() throws Exception {
1960         final String html = "";
1961         final HtmlPage page = loadPage(html);
1962         page.addAutoCloseable(null);
1963         page.cleanUp();
1964     }
1965 
1966     /**
1967      * @exception Exception If the test fails
1968      */
1969     @Test
1970     public void getBaseUrl() throws Exception {
1971         final String html = DOCTYPE_HTML
1972                 + "<html>\n"
1973                 + "<head></head>\n"
1974                 + "<body>body</body>\n"
1975                 + "</html>";
1976 
1977         HtmlPage page = loadPage(getBrowserVersion(), html, null, URL_FIRST);
1978         assertEquals(URL_FIRST.toExternalForm(), page.getBaseURL().toExternalForm());
1979 
1980         // see also org.htmlunit.javascript.host.html.HTMLDocumentTest.baseURI_noBaseTag()
1981         String path = "details/abc";
1982         page = loadPage(getBrowserVersion(), html, null, new URL(URL_FIRST.toString() + path));
1983         assertEquals(URL_FIRST.toExternalForm() + path, page.getBaseURL().toExternalForm());
1984 
1985         path = "details/abc?x=y&z=z";
1986         page = loadPage(getBrowserVersion(), html, null, new URL(URL_FIRST.toString() + path));
1987         assertEquals(URL_FIRST.toExternalForm() + path, page.getBaseURL().toExternalForm());
1988 
1989         path = "details/abc;jsessionid=42?x=y&z=z";
1990         page = loadPage(getBrowserVersion(), html, null, new URL(URL_FIRST.toString() + path));
1991         assertEquals(URL_FIRST.toExternalForm() + path, page.getBaseURL().toExternalForm());
1992     }
1993 }