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.parser;
16  
17  import java.net.URL;
18  import java.util.ArrayList;
19  import java.util.List;
20  
21  import org.apache.commons.lang3.builder.EqualsBuilder;
22  import org.apache.commons.lang3.builder.HashCodeBuilder;
23  import org.htmlunit.MockWebConnection;
24  import org.htmlunit.SimpleWebTestCase;
25  import org.htmlunit.WebClient;
26  import org.htmlunit.html.HtmlPage;
27  import org.htmlunit.junit.BrowserRunner;
28  import org.junit.Test;
29  import org.junit.runner.RunWith;
30  
31  /**
32   * Test class for {@link HTMLParserListener}.
33   *
34   * We probably don't need to check the details of the messages generated by the
35   * parser but just that we catch and "transmit" them.
36   *
37   * @author Marc Guillemot
38   * @author Ronald Brill
39   */
40  @RunWith(BrowserRunner.class)
41  public class HTMLParserListenerTest extends SimpleWebTestCase {
42      static class MessageInfo {
43          private boolean error_; // versus warning
44          private String message_;
45          private URL url_;
46          private String html_;
47          private int line_;
48          private int column_;
49  
50          /**
51           * Utility class to hold data.
52           * @param error the error
53           * @param message the message
54           * @param url the URL
55           * @param line the line number
56           * @param column the column number
57           * @param key Ignored value
58           */
59          MessageInfo(final boolean error, final String message, final URL url, final String html,
60                  final int line, final int column, final String key) {
61              error_ = error;
62              message_ = message;
63              url_ = url;
64              html_ = html;
65              line_ = line;
66              column_ = column;
67              // ignore key
68          }
69  
70          /**
71           * @see Object#toString()
72           */
73          @Override
74          public String toString() {
75              return message_ + " (" + url_ + " " + line_ + ":" + column_ + ")";
76          }
77  
78          /**
79           * Compares according to error, message, URL and line.
80           * @see Object#equals(Object)
81           */
82          @Override
83          public boolean equals(final Object obj) {
84              if (!(obj instanceof MessageInfo)) {
85                  return false;
86              }
87              final MessageInfo other = (MessageInfo) obj;
88              final EqualsBuilder builder = new EqualsBuilder();
89              builder.append(error_, other.error_);
90              builder.append(message_, other.message_);
91              builder.append(url_.toExternalForm(), other.url_.toExternalForm());
92              builder.append(html_, other.html_);
93              builder.append(line_, other.line_);
94              builder.append(column_, other.column_);
95              return builder.isEquals();
96          }
97  
98          /**
99           * @see Object#hashCode()
100          */
101         @Override
102         public int hashCode() {
103             final HashCodeBuilder builder = new HashCodeBuilder();
104             builder.append(error_);
105             builder.append(message_);
106             builder.append(url_);
107             builder.append(html_);
108             builder.append(line_);
109             builder.append(column_);
110             return builder.hashCode();
111         }
112     }
113 
114     /**
115      * This is the right test as we had before HtmlUnit-2.6 but due
116      * to an (accepted - at least in a first time -) regression in
117      * NekoHTML, it doesn't work anymore.
118      * testSimple_withWrongLineCol ensures that no other regression occurs here.
119      * @exception Exception If the test fails
120      */
121     @Test
122     public void simple() throws Exception {
123         testSimple(5, 8);
124     }
125 
126     /**
127      * Currently, NekoHtml doesn't deliver right information about the line
128      * for the warning. Let this test run with wrong expectation
129      * on line and col to avoid that other regression occur.
130      * @exception Exception If the test fails
131      */
132     @Test
133     public void simple_withWrongLineCol() throws Exception {
134         testSimple(5, 8);
135     }
136 
137     private void testSimple(final int line, final int col) throws Exception {
138         final String htmlContent =
139                 "<html>\n"
140                 + "<body>\n"
141                 + "<p>foo\n"
142                 + "</body>"
143                 + "\n</html>";
144 
145         final WebClient webClient = getWebClient();
146         assertNull(webClient.getHTMLParserListener());
147 
148         final List<MessageInfo> messages = new ArrayList<>();
149         final HTMLParserListener collecter = new HTMLParserListener() {
150 
151             @Override
152             public void error(final String message, final URL url, final String html,
153                     final int errorLine, final int column, final String key) {
154                 messages.add(new MessageInfo(true, message, url, html, errorLine, column, key));
155             }
156 
157             @Override
158             public void warning(final String message, final URL url, final String html,
159                     final int warningLine, final int column, final String key) {
160                 messages.add(new MessageInfo(false, message, url, html, warningLine, column, key));
161             }
162         };
163         webClient.setHTMLParserListener(collecter);
164 
165         final MockWebConnection webConnection = new MockWebConnection();
166         webConnection.setDefaultResponse(htmlContent);
167         webClient.setWebConnection(webConnection);
168 
169         final HtmlPage page = webClient.getPage(URL_FIRST);
170         assertEquals("foo", page.asNormalizedText());
171 
172         // ignore column and key
173         final MessageInfo expectedError = new MessageInfo(false,
174                 "End element <body> automatically closes element <p>.",
175                 URL_FIRST, null, line, col, null);
176 
177         assertEquals(1, messages.size());
178         assertEquals(expectedError, messages.get(0));
179     }
180 
181     /**
182      * Test parsing of a fragment.
183      * @exception Exception If the test fails
184      */
185     @Test
186     public void parseFragment() throws Exception {
187         final String htmlContent = "<html><head><title>foo</title>\n"
188                 + "<script>\n"
189                 + "function test() {\n"
190                 + "  var oNode = document.getElementById('middle');\n"
191                 + "  oNode.insertAdjacentHTML('afterEnd', '<div><span>before end</div>');\n"
192                 + "}\n"
193                 + "</script>\n"
194                 + "</head>\n"
195                 + "<body onload='test()'>\n"
196                 + "  <span id='middle' style='color: #ff0000'>\n"
197                 + "    inside\n"
198                 + "  </span>\n"
199                 + "</body></html>";
200 
201         final WebClient webClient = getWebClient();
202         assertNull(webClient.getHTMLParserListener());
203 
204         final List<MessageInfo> messages = new ArrayList<>();
205         final HTMLParserListener collecter = new HTMLParserListener() {
206 
207             @Override
208             public void error(final String message, final URL url, final String html,
209                     final int line, final int column, final String key) {
210                 messages.add(new MessageInfo(true, message, url, html, line, column, key));
211             }
212 
213             @Override
214             public void warning(final String message, final URL url, final String html,
215                     final int line, final int column, final String key) {
216                 messages.add(new MessageInfo(false, message, url, html, line, column, key));
217             }
218         };
219         webClient.setHTMLParserListener(collecter);
220 
221         final MockWebConnection webConnection = new MockWebConnection();
222         webConnection.setDefaultResponse(htmlContent);
223         webClient.setWebConnection(webConnection);
224 
225         final HtmlPage page = webClient.getPage(URL_FIRST);
226         assertEquals("foo", page.getTitleText());
227 
228         // ignore key
229         final MessageInfo expectedError = new MessageInfo(false,
230                 "End element <div> automatically closes element <span>.",
231                 URL_FIRST, "<div><span>before end</div>", 1, 28, null);
232 
233         assertEquals(1, messages.size());
234         assertEquals(expectedError, messages.get(0));
235     }
236 }