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.javascript;
16  
17  import static org.junit.Assert.fail;
18  
19  import java.net.MalformedURLException;
20  import java.net.URL;
21  import java.util.Collections;
22  
23  import org.htmlunit.FailingHttpStatusCodeException;
24  import org.htmlunit.MockWebConnection;
25  import org.htmlunit.ScriptException;
26  import org.htmlunit.WebClient;
27  import org.htmlunit.WebServerTestCase;
28  import org.htmlunit.html.HtmlPage;
29  import org.htmlunit.junit.BrowserRunner;
30  import org.htmlunit.util.MimeType;
31  import org.junit.Test;
32  import org.junit.runner.RunWith;
33  
34  /**
35   * Tests for {@link JavaScriptErrorListener}.
36   *
37   * @author Ronald Brill
38   * @author Marc Guillemot
39   */
40  @RunWith(BrowserRunner.class)
41  
42  public class JavascriptErrorListenerTest extends WebServerTestCase {
43  
44      /**
45       * Test for running without a JavaScript error listener.
46       *
47       * @throws Exception if the test fails
48       */
49      @Test
50      public void nullJavaScriptErrorListener() throws Exception {
51          final WebClient webClient = getWebClient();
52          webClient.getOptions().setThrowExceptionOnScriptError(false);
53          webClient.setJavaScriptErrorListener(null);
54          final MockWebConnection webConnection = new MockWebConnection();
55          final String errorContent = DOCTYPE_HTML + "<html><head><title>ERROR 500</title></head><body></body></html>";
56          webConnection.setResponse(URL_SECOND, errorContent, 500, "BOOM", MimeType.TEXT_HTML, Collections.emptyList());
57  
58          // test script exception
59          String content = DOCTYPE_HTML
60              + "<html><head><title>Throw JavaScript Error</title>\n"
61              + "<script>unknown.foo();</script></head>\n"
62              + "<body></body></html>";
63          webConnection.setResponse(URL_FIRST, content);
64          webClient.setWebConnection(webConnection);
65          webClient.getPage(URL_FIRST);
66  
67          // test load script error
68          content = DOCTYPE_HTML
69              + "<html><head><title>Throw JavaScript Error</title>\n"
70              + "<script src='" + URL_SECOND + "' type='text/javascript'></script></head>\n"
71              + "<body></body></html>";
72          webConnection.setResponse(URL_FIRST, content);
73          try {
74              webClient.getPage(URL_FIRST);
75              fail("FailingHttpStatusCodeException expected");
76          }
77          catch (final FailingHttpStatusCodeException e) {
78              // expected
79          }
80  
81          // test malformed script url error
82          content = DOCTYPE_HTML
83              + "<html><head><title>Throw JavaScript Error</title>\n"
84              + "<script src='unknown://nowhere' type='text/javascript'></script></head>\n"
85              + "<body></body></html>";
86          webConnection.setResponse(URL_FIRST, content);
87          webClient.getPage(URL_FIRST);
88  
89          // test timeout error
90          webClient.setJavaScriptTimeout(100);
91  
92          content = DOCTYPE_HTML
93              + "<html><head><title>Throw JavaScript Timeout Error</title>\n"
94              + "<script>while(1) {}</script></head>\n"
95              + "<body></body></html>";
96          webConnection.setResponse(URL_FIRST, content);
97          webClient.getPage(URL_FIRST);
98      }
99  
100     /**
101      * Listener should capture script exception.
102      * @throws Exception if the test fails
103      */
104     @Test
105     public void listenForScriptException() throws Exception {
106         final WebClient webClient = getWebClient();
107         webClient.getOptions().setThrowExceptionOnScriptError(false);
108         final CollectingJavaScriptErrorListener javaScriptErrorListener = new CollectingJavaScriptErrorListener();
109         webClient.setJavaScriptErrorListener(javaScriptErrorListener);
110 
111         // test script exception
112         final String html = DOCTYPE_HTML
113             + "<html><head><title>Throw JavaScript Error</title>"
114             + "<script>unknown.foo();</script></head>"
115             + "<body></body></html>";
116         loadPage(html);
117 
118         assertEquals("", javaScriptErrorListener.getWarnings());
119         assertEquals("org.htmlunit.ScriptException: "
120                 + "ReferenceError: \"unknown\" is not defined. "
121                 + "(script in http://localhost:" + PORT + "/ from (2, 58) to (2, 81)#2)",
122                 javaScriptErrorListener.getScriptExceptions());
123         assertEquals("", javaScriptErrorListener.getLoadScriptErrors());
124         assertEquals("", javaScriptErrorListener.getMalformedScriptURLErrors());
125         assertEquals("", javaScriptErrorListener.getTimeoutErrors());
126     }
127 
128     /**
129      * Listener should capture script exception.
130      * @throws Exception if the test fails
131      */
132     @Test
133     public void listenForLoadScriptError() throws Exception {
134         final WebClient webClient = getWebClient();
135         webClient.getOptions().setThrowExceptionOnScriptError(false);
136         final CollectingJavaScriptErrorListener javaScriptErrorListener = new CollectingJavaScriptErrorListener();
137         webClient.setJavaScriptErrorListener(javaScriptErrorListener);
138 
139         getMockWebConnection().setDefaultResponse("", 500, "Server Error", MimeType.TEXT_HTML);
140 
141         final String html = DOCTYPE_HTML
142             + "<html><head>\n"
143             + "<script src='notExisting.js' type='text/javascript'></script></head>\n"
144             + "<body></body></html>";
145 
146         try {
147             loadPage(html);
148             fail("FailingHttpStatusCodeException expected");
149         }
150         catch (final FailingHttpStatusCodeException e) {
151             // expected
152         }
153 
154         assertEquals("", javaScriptErrorListener.getScriptExceptions());
155         assertEquals(URL_FIRST + "notExisting.js, "
156                 + "org.htmlunit.FailingHttpStatusCodeException: "
157                 + "500 Server Error for " + URL_FIRST + "notExisting.js",
158                 javaScriptErrorListener.getLoadScriptErrors());
159         assertEquals("", javaScriptErrorListener.getMalformedScriptURLErrors());
160         assertEquals("", javaScriptErrorListener.getTimeoutErrors());
161     }
162 
163     /**
164      * Listener should capture script exception.
165      * @throws Exception if the test fails
166      */
167     @Test
168     public void listenForMalformedScriptUrl() throws Exception {
169         final WebClient webClient = getWebClient();
170         webClient.getOptions().setThrowExceptionOnScriptError(false);
171         final CollectingJavaScriptErrorListener javaScriptErrorListener = new CollectingJavaScriptErrorListener();
172         webClient.setJavaScriptErrorListener(javaScriptErrorListener);
173 
174         final String html = DOCTYPE_HTML
175                 + "<html>\n"
176                 + "<head><title>Throw JavaScript Error</title>\n"
177                 + "<script src='unknown://nowhere' type='text/javascript'></script>\n"
178                 + "</head>\n"
179                 + "<body></body>\n"
180                 + "</html>";
181 
182         loadPage(html);
183 
184         assertEquals("", javaScriptErrorListener.getWarnings());
185         assertEquals("", javaScriptErrorListener.getScriptExceptions());
186         assertEquals("", javaScriptErrorListener.getLoadScriptErrors());
187         assertEquals("unknown://nowhere, java.net.MalformedURLException: unknown protocol: 'unknown'",
188                     javaScriptErrorListener.getMalformedScriptURLErrors());
189         assertEquals("", javaScriptErrorListener.getTimeoutErrors());
190     }
191 
192     /**
193      * Listener should capture timeout errors.
194      * Configured with a timeout as the build server seemed to have problem with this test from time to time.
195      * @throws Exception if the test fails
196      */
197     @Test(timeout = 10_000)
198     public void listenForTimeoutError() throws Exception {
199         final WebClient webClient = getWebClient();
200         webClient.getOptions().setThrowExceptionOnScriptError(false);
201         final CollectingJavaScriptErrorListener javaScriptErrorListener = new CollectingJavaScriptErrorListener();
202         webClient.setJavaScriptErrorListener(javaScriptErrorListener);
203         webClient.setJavaScriptTimeout(100);
204 
205         final String html = DOCTYPE_HTML
206             + "<html><head><title>Throw JavaScript Timeout Error</title>\n"
207             + "<script>while(1) {}</script></head>\n"
208             + "<body></body></html>";
209 
210         loadPage(html);
211 
212         assertEquals("", javaScriptErrorListener.getWarnings());
213         assertEquals("", javaScriptErrorListener.getScriptExceptions());
214         assertEquals("", javaScriptErrorListener.getLoadScriptErrors());
215         assertEquals("", javaScriptErrorListener.getMalformedScriptURLErrors());
216         assertEquals("Timeout allowed: 100", javaScriptErrorListener.getTimeoutErrors());
217     }
218 }
219 
220 class CollectingJavaScriptErrorListener implements JavaScriptErrorListener {
221     private final StringBuilder warnings_ = new StringBuilder();
222     private final StringBuilder scriptExceptions_ = new StringBuilder();
223     private final StringBuilder timeoutErrors_ = new StringBuilder();
224     private final StringBuilder loadScriptErrors_ = new StringBuilder();
225     private final StringBuilder malformedScriptURLErrors_ = new StringBuilder();
226 
227     @Override
228     public void warn(final String message, final String sourceName,
229             final int line, final String lineSource, final int lineOffset) {
230         warnings_.append(message);
231     }
232 
233     @Override
234     public void loadScriptError(final HtmlPage page, final URL scriptUrl, final Exception exception) {
235         loadScriptErrors_.append(scriptUrl + ", " + exception);
236     }
237 
238     @Override
239     public void malformedScriptURL(final HtmlPage page, final String url,
240             final MalformedURLException malformedURLException) {
241         malformedScriptURLErrors_.append(url + ", " + malformedURLException);
242     }
243 
244     @Override
245     public void scriptException(final HtmlPage page, final ScriptException scriptException) {
246         scriptExceptions_.append(scriptException.toString());
247     }
248 
249     @Override
250     public void timeoutError(final HtmlPage page, final long allowedTime, final long executionTime) {
251         timeoutErrors_.append("Timeout allowed: " + allowedTime);
252     }
253 
254     public String getWarnings() {
255         return warnings_.toString();
256     }
257 
258     public String getScriptExceptions() {
259         return scriptExceptions_.toString();
260     }
261 
262     public String getLoadScriptErrors() {
263         return loadScriptErrors_.toString();
264     }
265 
266     public String getMalformedScriptURLErrors() {
267         return malformedScriptURLErrors_.toString();
268     }
269 
270     public String getTimeoutErrors() {
271         return timeoutErrors_.toString();
272     }
273 }