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.host.xml;
16  
17  import java.io.IOException;
18  import java.io.OutputStreamWriter;
19  import java.io.Writer;
20  import java.net.URL;
21  import java.util.ArrayList;
22  import java.util.Arrays;
23  import java.util.HashMap;
24  import java.util.List;
25  import java.util.Map;
26  
27  import javax.servlet.Servlet;
28  import javax.servlet.ServletException;
29  import javax.servlet.ServletOutputStream;
30  import javax.servlet.http.HttpServlet;
31  import javax.servlet.http.HttpServletRequest;
32  import javax.servlet.http.HttpServletResponse;
33  
34  import org.htmlunit.HttpMethod;
35  import org.htmlunit.MiniServer;
36  import org.htmlunit.MockWebConnection;
37  import org.htmlunit.WebDriverTestCase;
38  import org.htmlunit.WebTestCase;
39  import org.htmlunit.http.HttpStatus;
40  import org.htmlunit.junit.BrowserRunner;
41  import org.htmlunit.junit.annotation.Alerts;
42  import org.htmlunit.junit.annotation.Retry;
43  import org.htmlunit.util.MimeType;
44  import org.htmlunit.util.NameValuePair;
45  import org.junit.After;
46  import org.junit.Before;
47  import org.junit.Test;
48  import org.junit.experimental.runners.Enclosed;
49  import org.junit.runner.RunWith;
50  import org.openqa.selenium.By;
51  import org.openqa.selenium.WebDriver;
52  import org.openqa.selenium.WebDriverException;
53  
54  /**
55   * Tests for the LifeCycle events for XMLHttpRequests.
56   * readystatechange
57   * loadstart - whenever the loading is started. will always have a payload of 0.
58   * loadend - whenever the loading is finished. Will be triggered after error as well.
59   * progress - periodic updates that the transfer is still in progress. will only be triggered in async requests.
60   * abort - aborts the scheduled request. will only be triggerd in async requests.
61   * error - on network errors. server status will be ignored for this.
62   * timeout - when the request is terminated because of the timeout
63   * (only available in async requests, otherwise xhr.send will fail)
64   *
65   * The tests are split between sync (full-event cycle test) and async (each event is tested on it's own).
66   * This is mainly done because we cannot reliably handle the amount & speed of the alerts if everything is
67   * executed together (Chrome did work in tests, FF & IE did not).
68   *
69   * @author Thorsten Wendelmuth
70   * @author Ronald Brill
71   *
72   */
73  @RunWith(Enclosed.class)
74  public final class XMLHttpRequestLifeCycleTest {
75      private static final String SUCCESS_URL = "/xmlhttprequest/success.html";
76      private static final String SUCCESS_WITHOUT_ORIGIN_URL = "/xmlhttprequest/success_without_origin.html";
77      private static final String ERROR_403_URL = "/xmlhttprequest/error_403.html";
78      private static final String ERROR_500_URL = "/xmlhttprequest/error_500.html";
79      private static final String PREFLIGHT_ERROR_403_URL = "/xmlhttprequest/preflighterror_403.html";
80      private static final String PREFLIGHT_ERROR_500_URL = "/xmlhttprequest/preflighterror_500.html";
81      private static final String TIMEOUT_URL = "/xmlhttprequest/timeout.html";
82  
83      private static final String RETURN_XML = "<xml>\n"
84              + "<content>htmlunit</content>\n"
85              + "<content>xmlhttpRequest</content>\n"
86              + "</xml>";
87  
88      private enum State {
89          LOAD_START("loadstart"), LOAD("load"), LOAD_END("loadend"), PROGRESS("progress"), ERROR("error"),
90          ABORT("abort"), READY_STATE_CHANGE("readystatechange"), TIMEOUT("timeout");
91  
92          private final String eventName_;
93  
94          State(final String eventName) {
95              eventName_ = eventName;
96          }
97  
98          public String getEventName_() {
99              return eventName_;
100         }
101     }
102 
103     private enum Mode {
104         ASYNC(true, false), SYNC(false, false), ASYNC_ON_KEYWORD(true, true), SYNC_ON_KEYWORD(false, true);
105 
106         private final boolean async_;
107         private final boolean useOnKeyword_;
108 
109         Mode(final boolean async, final boolean useOnKeyword) {
110             async_ = async;
111             useOnKeyword_ = useOnKeyword;
112         }
113 
114         public boolean isAsync() {
115             return async_;
116         }
117 
118         public boolean isUseOnKeyword() {
119             return useOnKeyword_;
120         }
121     }
122 
123     private enum Execution {
124         ONLY_SEND, SEND_ABORT, DONE_ABORT, NETWORK_ERROR, ERROR_403, ERROR_500, TIMEOUT,
125         ONLY_SEND_PREFLIGHT, ONLY_SEND_PREFLIGHT_FORBIDDEN,
126         WITHOUT_ORIGIN, WITHOUT_ORIGIN_PREFLIGHT,
127         NETWORK_ERROR_PREFLIGHT,
128         ERROR_403_PREFLIGHT, ERROR_403_DURING_PREFLIGHT,
129         ERROR_500_PREFLIGHT, ERROR_500_DURING_PREFLIGHT
130     }
131 
132     /**
133      * Test using our JettyServer.
134      */
135     @RunWith(BrowserRunner.class)
136     public static class JettyServerTest extends WebDriverTestCase {
137 
138         /**
139          * Helper servlet.
140          */
141         public static class Xml200Servlet extends HttpServlet {
142 
143             @Override
144             protected void doOptions(final HttpServletRequest request, final HttpServletResponse response) {
145                 response.setHeader("Access-Control-Allow-Origin", "*");
146                 response.setHeader("Access-Control-Allow-Methods", "*");
147                 response.setHeader("Access-Control-Allow-Headers", "X-PINGOTHER");
148             }
149 
150             @Override
151             protected void doGet(final HttpServletRequest request, final HttpServletResponse response)
152                     throws ServletException, IOException {
153                 response.setHeader("Access-Control-Allow-Origin", "*");
154                 response.setHeader("Access-Control-Allow-Methods", "*");
155                 response.setHeader("Access-Control-Allow-Headers", "X-PINGOTHER");
156 
157                 response.setContentType(MimeType.TEXT_XML);
158                 response.setContentLength(RETURN_XML.length());
159                 response.setStatus(HttpStatus.OK_200);
160                 final ServletOutputStream outputStream = response.getOutputStream();
161                 try (Writer writer = new OutputStreamWriter(outputStream)) {
162                     writer.write(RETURN_XML);
163                 }
164             }
165         }
166 
167         /**
168          * Helper servlet.
169          */
170         public static class Xml200ServletWithoutOriginHeader extends HttpServlet {
171 
172             @Override
173             protected void doOptions(final HttpServletRequest request, final HttpServletResponse response) {
174                 response.setHeader("Access-Control-Allow-Origin", "*");
175                 response.setHeader("Access-Control-Allow-Methods", "*");
176                 response.setHeader("Access-Control-Allow-Headers", "X-PINGOTHER");
177             }
178 
179             @Override
180             protected void doGet(final HttpServletRequest request, final HttpServletResponse response)
181                     throws ServletException, IOException {
182                 response.setContentType(MimeType.TEXT_XML);
183                 response.setContentLength(RETURN_XML.length());
184                 response.setStatus(HttpStatus.OK_200);
185                 final ServletOutputStream outputStream = response.getOutputStream();
186                 try (Writer writer = new OutputStreamWriter(outputStream)) {
187                     writer.write(RETURN_XML);
188                 }
189             }
190         }
191 
192         /**
193          * Helper servlet.
194          */
195         public static class Xml403Servlet extends HttpServlet {
196 
197             @Override
198             protected void doOptions(final HttpServletRequest request, final HttpServletResponse response) {
199                 response.setHeader("Access-Control-Allow-Origin", "*");
200                 response.setHeader("Access-Control-Allow-Methods", "*");
201                 response.setHeader("Access-Control-Allow-Headers", "X-PINGOTHER");
202             }
203 
204             @Override
205             protected void doGet(final HttpServletRequest request, final HttpServletResponse response)
206                     throws ServletException, IOException {
207                 response.setHeader("Access-Control-Allow-Origin", "*");
208                 response.setHeader("Access-Control-Allow-Methods", "*");
209                 response.setHeader("Access-Control-Allow-Headers", "X-PINGOTHER");
210 
211                 response.setContentType(MimeType.TEXT_XML);
212                 response.setContentLength(RETURN_XML.length());
213                 response.setStatus(HttpStatus.FORBIDDEN_403);
214                 final ServletOutputStream outputStream = response.getOutputStream();
215                 try (Writer writer = new OutputStreamWriter(outputStream)) {
216                     writer.write(RETURN_XML);
217                 }
218             }
219         }
220 
221         /**
222          * Helper servlet.
223          */
224         public static class Xml500Servlet extends HttpServlet {
225 
226             @Override
227             protected void doOptions(final HttpServletRequest request, final HttpServletResponse response) {
228                 response.setHeader("Access-Control-Allow-Origin", "*");
229                 response.setHeader("Access-Control-Allow-Methods", "*");
230                 response.setHeader("Access-Control-Allow-Headers", "X-PINGOTHER");
231             }
232 
233             @Override
234             protected void doGet(final HttpServletRequest request, final HttpServletResponse response)
235                     throws ServletException, IOException {
236                 response.setHeader("Access-Control-Allow-Origin", "*");
237                 response.setHeader("Access-Control-Allow-Methods", "*");
238                 response.setHeader("Access-Control-Allow-Headers", "X-PINGOTHER");
239 
240                 response.setContentType(MimeType.TEXT_XML);
241                 response.setContentLength(RETURN_XML.length());
242                 response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR_500);
243                 final ServletOutputStream outputStream = response.getOutputStream();
244                 try (Writer writer = new OutputStreamWriter(outputStream)) {
245                     writer.write(RETURN_XML);
246                 }
247             }
248         }
249 
250         /**
251          * Helper servlet.
252          */
253         public static class Preflight403Servlet extends HttpServlet {
254 
255             @Override
256             protected void doOptions(final HttpServletRequest request, final HttpServletResponse response) {
257                 response.setStatus(HttpStatus.FORBIDDEN_403);
258             }
259         }
260 
261         /**
262          * Helper servlet.
263          */
264         public static class Preflight500Servlet extends HttpServlet {
265 
266             @Override
267             protected void doOptions(final HttpServletRequest request, final HttpServletResponse response) {
268                 response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR_500);
269             }
270         }
271 
272         /**
273          * Helper servlet.
274          */
275         public static class XmlTimeoutServlet extends HttpServlet {
276 
277             @Override
278             protected void doGet(final HttpServletRequest req, final HttpServletResponse response)
279                     throws ServletException, IOException {
280                 response.setHeader("Access-Control-Allow-Origin", "*");
281                 response.setHeader("Access-Control-Allow-Methods", "*");
282                 response.setHeader("Access-Control-Allow-Headers", "X-PINGOTHER");
283 
284                 response.setContentType(MimeType.TEXT_XML);
285                 response.setContentLength(RETURN_XML.length());
286                 response.setStatus(HttpStatus.OK_200);
287                 final ServletOutputStream outputStream = response.getOutputStream();
288                 try (Writer writer = new OutputStreamWriter(outputStream)) {
289                     writer.flush();
290                     Thread.sleep(500);
291                     writer.write(RETURN_XML);
292                 }
293                 catch (final Exception ignored) {
294                     // ignore
295                 }
296             }
297         }
298 
299         private final Map<String, Class<? extends Servlet>> servlets_ = new HashMap<>();
300 
301         /**
302          * Setup our servlets.
303          */
304         @Before
305         public void prepareTestingServlets() {
306             servlets_.put(SUCCESS_URL, Xml200Servlet.class);
307             servlets_.put(SUCCESS_WITHOUT_ORIGIN_URL, Xml200ServletWithoutOriginHeader.class);
308             servlets_.put(ERROR_403_URL, Xml403Servlet.class);
309             servlets_.put(ERROR_500_URL, Xml500Servlet.class);
310             servlets_.put(PREFLIGHT_ERROR_403_URL, Preflight403Servlet.class);
311             servlets_.put(PREFLIGHT_ERROR_500_URL, Preflight500Servlet.class);
312             servlets_.put(TIMEOUT_URL, XmlTimeoutServlet.class);
313         }
314 
315         /**
316          * @throws Exception if the test fails
317          */
318         @Test
319         @Alerts({"readystatechange_1_0_true", "open-done: 1_0",
320                  "readystatechange_4_200_true", "load_4_200_false",
321                  "loadend_4_200_false", "send-done: 4_200"})
322         public void addEventListener_sync() throws Exception {
323             final WebDriver driver = loadPage2(buildHtml(Mode.SYNC, Execution.ONLY_SEND),
324                     URL_FIRST, servlets_);
325             verify(() -> extractLog(driver), String.join("\n", getExpectedAlerts()));
326         }
327 
328         /**
329          * @throws Exception if the test fails
330          */
331         @Test
332         @Alerts({"readystatechange_1_0_true", "open-done: 1_0",
333                  "readystatechange_4_200_true", "load_4_200_false",
334                  "loadend_4_200_false", "send-done: 4_200"})
335         public void addEventListener_sync_preflight() throws Exception {
336             final WebDriver driver = loadPage2(buildHtml(Mode.SYNC, Execution.ONLY_SEND_PREFLIGHT),
337                     URL_FIRST, servlets_, servlets_);
338             verify(() -> extractLog(driver), String.join("\n", getExpectedAlerts()));
339         }
340 
341         /**
342          * @throws Exception if the test fails
343          */
344         @Test
345         @Alerts(DEFAULT = {"readystatechange_1_0_true", "open-done: 1_0",
346                            "ExceptionThrown"},
347                 FF = {"readystatechange_1_0_true", "open-done: 1_0",
348                       "readystatechange_4_0_true", "error_4_0_false",
349                       "loadend_4_0_false", "ExceptionThrown"},
350                 FF_ESR = {"readystatechange_1_0_true", "open-done: 1_0",
351                           "readystatechange_4_0_true", "error_4_0_false",
352                           "loadend_4_0_false", "ExceptionThrown"})
353         public void addEventListener_sync_preflight_forbidden() throws Exception {
354             final WebDriver driver = loadPage2(buildHtml(Mode.SYNC, Execution.ONLY_SEND_PREFLIGHT_FORBIDDEN),
355                     URL_FIRST, servlets_, servlets_);
356             verify(() -> extractLog(driver), String.join("\n", getExpectedAlerts()));
357         }
358 
359         /**
360          * @throws Exception if the test fails
361          */
362         @Test
363         @Alerts(DEFAULT = {"readystatechange_1_0_true", "open-done: 1_0", "ExceptionThrown"},
364                 FF = {"readystatechange_1_0_true", "open-done: 1_0",
365                       "readystatechange_4_0_true", "error_4_0_false", "loadend_4_0_false",
366                       "ExceptionThrown"},
367                 FF_ESR = {"readystatechange_1_0_true", "open-done: 1_0",
368                           "readystatechange_4_0_true", "error_4_0_false", "loadend_4_0_false",
369                           "ExceptionThrown"})
370         public void addEventListener_sync_without_origin() throws Exception {
371             final WebDriver driver = loadPage2(buildHtml(Mode.SYNC, Execution.WITHOUT_ORIGIN),
372                     URL_FIRST, servlets_, servlets_);
373             verify(() -> extractLog(driver), String.join("\n", getExpectedAlerts()));
374         }
375 
376         /**
377          * @throws Exception if the test fails
378          */
379         @Test
380         @Alerts(DEFAULT = {"readystatechange_1_0_true", "open-done: 1_0", "ExceptionThrown"},
381                 FF = {"readystatechange_1_0_true", "open-done: 1_0",
382                       "readystatechange_4_0_true", "error_4_0_false", "loadend_4_0_false",
383                       "ExceptionThrown"},
384                 FF_ESR = {"readystatechange_1_0_true", "open-done: 1_0",
385                           "readystatechange_4_0_true", "error_4_0_false", "loadend_4_0_false",
386                           "ExceptionThrown"})
387         public void addEventListener_sync_preflight_without_origin() throws Exception {
388             final WebDriver driver = loadPage2(buildHtml(Mode.SYNC, Execution.WITHOUT_ORIGIN_PREFLIGHT),
389                     URL_FIRST, servlets_, servlets_);
390             verify(() -> extractLog(driver), String.join("\n", getExpectedAlerts()));
391         }
392 
393         /**
394          * @throws Exception if the test fails
395          */
396         @Test
397         @Alerts({"readystatechange_1_0_true", "open-done: 1_0", "readystatechange_4_200_true",
398                  "load_4_200_false", "loadend_4_200_false", "send-done: 4_200", "abort-done: 0_0"})
399         public void addEventListener_sync_abortTriggered() throws Exception {
400             final WebDriver driver = loadPage2(buildHtml(Mode.SYNC, Execution.SEND_ABORT), URL_FIRST,
401                     servlets_);
402             verify(() -> extractLog(driver), String.join("\n", getExpectedAlerts()));
403         }
404 
405         /**
406          * @throws Exception if the test fails
407          */
408         @Test
409         @Alerts(DEFAULT = {"readystatechange_1_0_true", "open-done: 1_0", "readystatechange_4_200_true",
410                            "abort-done: 0_0", "ExceptionThrown"},
411                 FF = {"readystatechange_1_0_true", "open-done: 1_0", "readystatechange_4_200_true",
412                       "abort-done: 0_0", "load_0_0_false", "loadend_0_0_false", "send-done: 0_0"},
413                 FF_ESR = {"readystatechange_1_0_true", "open-done: 1_0", "readystatechange_4_200_true",
414                           "abort-done: 0_0", "load_0_0_false", "loadend_0_0_false", "send-done: 0_0"})
415         public void addEventListener_sync_abortAfterDoneTriggered() throws Exception {
416             final WebDriver driver = loadPage2(buildHtml(Mode.SYNC, Execution.DONE_ABORT), URL_FIRST,
417                     servlets_);
418             verify(() -> extractLog(driver), String.join("\n", getExpectedAlerts()));
419         }
420 
421         /**
422          * @throws Exception if the test fails
423          */
424         @Test
425         @Alerts(DEFAULT = {"readystatechange_1_0_true", "open-done: 1_0", "ExceptionThrown"},
426                 FF = {"readystatechange_1_0_true", "open-done: 1_0", "readystatechange_4_0_true",
427                       "error_4_0_false", "loadend_4_0_false", "ExceptionThrown"},
428                 FF_ESR = {"readystatechange_1_0_true", "open-done: 1_0", "readystatechange_4_0_true",
429                           "error_4_0_false", "loadend_4_0_false", "ExceptionThrown"})
430         public void addEventListener_sync_networkError() throws Exception {
431             try {
432                 loadPage2(buildHtml(Mode.SYNC, Execution.NETWORK_ERROR), URL_FIRST, servlets_);
433             }
434             catch (final WebDriverException e) {
435                 if (useRealBrowser()) {
436                     // we only expect the error to be thrown in htmlunit scenarios.
437                     throw e;
438                 }
439             }
440             finally {
441                 verify(() -> extractLog(getWebDriver()), String.join("\n", getExpectedAlerts()));
442             }
443         }
444 
445         /**
446          * @throws Exception if the test fails
447          */
448         @Test
449         @Alerts(DEFAULT = {"readystatechange_1_0_true", "open-done: 1_0", "ExceptionThrown"},
450                 FF = {"readystatechange_1_0_true", "open-done: 1_0", "readystatechange_4_0_true",
451                       "error_4_0_false", "loadend_4_0_false", "ExceptionThrown"},
452                 FF_ESR = {"readystatechange_1_0_true", "open-done: 1_0", "readystatechange_4_0_true",
453                           "error_4_0_false", "loadend_4_0_false", "ExceptionThrown"})
454         public void addEventListener_sync_networkError_preflight() throws Exception {
455             try {
456                 loadPage2(buildHtml(Mode.SYNC, Execution.NETWORK_ERROR_PREFLIGHT), URL_FIRST, servlets_, servlets_);
457             }
458             catch (final WebDriverException e) {
459                 if (useRealBrowser()) {
460                     // we only expect the error to be thrown in htmlunit scenarios.
461                     throw e;
462                 }
463             }
464             finally {
465                 verify(() -> extractLog(getWebDriver()), String.join("\n", getExpectedAlerts()));
466             }
467         }
468 
469         /**
470          * @throws Exception if the test fails
471          */
472         @Test
473         @Alerts({"readystatechange_1_0_true", "open-done: 1_0", "readystatechange_4_403_true",
474                  "load_4_403_false", "loadend_4_403_false", "send-done: 4_403"})
475         public void addEventListener_sync_Error403() throws Exception {
476             final WebDriver driver = loadPage2(buildHtml(Mode.SYNC, Execution.ERROR_403), URL_FIRST,
477                     servlets_);
478             verify(() -> extractLog(driver), String.join("\n",
479                     getExpectedAlerts()), DEFAULT_WAIT_TIME.multipliedBy(10));
480         }
481 
482         /**
483          * @throws Exception if the test fails
484          */
485         @Test
486         @Alerts({"readystatechange_1_0_true", "open-done: 1_0", "readystatechange_4_403_true",
487                  "load_4_403_false", "loadend_4_403_false", "send-done: 4_403"})
488         public void addEventListener_sync_Error403_preflight() throws Exception {
489             final WebDriver driver = loadPage2(buildHtml(Mode.SYNC, Execution.ERROR_403_PREFLIGHT), URL_FIRST,
490                     servlets_, servlets_);
491             verify(() -> extractLog(driver), String.join("\n", getExpectedAlerts()));
492         }
493 
494         /**
495          * @throws Exception if the test fails
496          */
497         @Test
498         @Alerts(DEFAULT = {"readystatechange_1_0_true", "open-done: 1_0", "ExceptionThrown"},
499                 FF = {"readystatechange_1_0_true", "open-done: 1_0",
500                       "readystatechange_4_0_true", "error_4_0_false", "loadend_4_0_false",
501                       "ExceptionThrown"},
502                 FF_ESR = {"readystatechange_1_0_true", "open-done: 1_0",
503                           "readystatechange_4_0_true", "error_4_0_false", "loadend_4_0_false",
504                           "ExceptionThrown"})
505         public void addEventListener_sync_Error403_during_preflight() throws Exception {
506             final WebDriver driver = loadPage2(buildHtml(Mode.SYNC, Execution.ERROR_403_DURING_PREFLIGHT), URL_FIRST,
507                     servlets_, servlets_);
508             verify(() -> extractLog(driver), String.join("\n", getExpectedAlerts()));
509         }
510 
511         /**
512          * @throws Exception if the test fails
513          */
514         @Test
515         @Alerts({"readystatechange_1_0_true", "open-done: 1_0", "readystatechange_4_500_true",
516                  "load_4_500_false", "loadend_4_500_false", "send-done: 4_500"})
517         public void addEventListener_sync_Error500() throws Exception {
518             final WebDriver driver = loadPage2(buildHtml(Mode.SYNC, Execution.ERROR_500), URL_FIRST,
519                     servlets_);
520             verify(() -> extractLog(driver), String.join("\n", getExpectedAlerts()),
521                     DEFAULT_WAIT_TIME.multipliedBy(10));
522         }
523 
524         /**
525          * @throws Exception if the test fails
526          */
527         @Test
528         @Alerts({"readystatechange_1_0_true", "open-done: 1_0", "readystatechange_4_500_true",
529                  "load_4_500_false", "loadend_4_500_false", "send-done: 4_500"})
530         public void addEventListener_sync_Error500_preflight() throws Exception {
531             final WebDriver driver = loadPage2(buildHtml(Mode.SYNC, Execution.ERROR_500_PREFLIGHT), URL_FIRST,
532                     servlets_, servlets_);
533             verify(() -> extractLog(driver), String.join("\n", getExpectedAlerts()));
534         }
535 
536         /**
537          * @throws Exception if the test fails
538          */
539         @Test
540         @Alerts(DEFAULT = {"readystatechange_1_0_true", "open-done: 1_0", "ExceptionThrown"},
541                 FF = {"readystatechange_1_0_true", "open-done: 1_0",
542                       "readystatechange_4_0_true", "error_4_0_false", "loadend_4_0_false",
543                       "ExceptionThrown"},
544                 FF_ESR = {"readystatechange_1_0_true", "open-done: 1_0",
545                           "readystatechange_4_0_true", "error_4_0_false", "loadend_4_0_false",
546                           "ExceptionThrown"})
547         public void addEventListener_sync_Error500_during_preflight() throws Exception {
548             final WebDriver driver = loadPage2(buildHtml(Mode.SYNC, Execution.ERROR_500_DURING_PREFLIGHT), URL_FIRST,
549                     servlets_, servlets_);
550             verify(() -> extractLog(driver), String.join("\n", getExpectedAlerts()));
551         }
552 
553         /**
554          * @throws Exception if the test fails
555          */
556         @Test
557         @Alerts({"readystatechange_1_0_true", "open-done: 1_0", "ExceptionThrown"})
558         public void addEventListener_sync_timeout() throws Exception {
559             // that's invalid. You cannot set timeout for synced requests. Will throw an
560             // exception only triggers readystatechange
561             try {
562                 loadPage2(buildHtml(Mode.SYNC, Execution.TIMEOUT), URL_FIRST, servlets_);
563             }
564             catch (final WebDriverException e) {
565                 if (useRealBrowser()) {
566                     // we only expect the error to be thrown in htmlunit scenarios.
567                     throw e;
568                 }
569             }
570             finally {
571                 verify(() -> extractLog(getWebDriver()), String.join("\n", getExpectedAlerts()));
572             }
573         }
574 
575         /**
576          * @throws Exception if the test fails
577          */
578         @Test
579         @Alerts({"readystatechange_1_0_true", "open-done: 1_0", "loadstart_1_0_false",
580                  "send-done: 1_0", "readystatechange_2_200_true", "readystatechange_3_200_true",
581                  "progress_3_200_false", "readystatechange_4_200_true", "load_4_200_false",
582                  "loadend_4_200_false"})
583         public void addEventListener_async() throws Exception {
584             final WebDriver driver = loadPage2(buildHtml(Mode.ASYNC, Execution.ONLY_SEND),
585                     URL_FIRST, servlets_);
586             verify(() -> extractLog(driver), String.join("\n", getExpectedAlerts()));
587         }
588 
589         /**
590          * @throws Exception if the test fails
591          */
592         @Test
593         @Alerts({"readystatechange_1_0_true", "open-done: 1_0", "loadstart_1_0_false",
594                  "send-done: 1_0", "readystatechange_2_200_true", "readystatechange_3_200_true",
595                  "progress_3_200_false", "readystatechange_4_200_true",
596                  "load_4_200_false", "loadend_4_200_false"})
597         public void addEventListener_async_preflight() throws Exception {
598             final WebDriver driver = loadPage2(buildHtml(Mode.ASYNC, Execution.ONLY_SEND_PREFLIGHT),
599                     URL_FIRST, servlets_, servlets_);
600             verify(() -> extractLog(driver), String.join("\n", getExpectedAlerts()));
601         }
602 
603         /**
604          * @throws Exception if the test fails
605          */
606         @Test
607         @Alerts({"readystatechange_1_0_true", "open-done: 1_0", "loadstart_1_0_false",
608                  "send-done: 1_0", "readystatechange_4_0_true", "error_4_0_false",
609                  "loadend_4_0_false"})
610         public void addEventListener_async_preflight_forbidden() throws Exception {
611             final WebDriver driver = loadPage2(buildHtml(Mode.ASYNC, Execution.ONLY_SEND_PREFLIGHT_FORBIDDEN),
612                     URL_FIRST, servlets_, servlets_);
613             verify(() -> extractLog(driver), String.join("\n", getExpectedAlerts()));
614         }
615 
616         /**
617          * @throws Exception if the test fails
618          */
619         @Test
620         @Alerts({"readystatechange_1_0_true", "open-done: 1_0",
621                  "loadstart_1_0_false", "send-done: 1_0", "readystatechange_4_0_true",
622                  "error_4_0_false", "loadend_4_0_false"})
623         public void addEventListener_async_without_origin() throws Exception {
624             final WebDriver driver = loadPage2(buildHtml(Mode.ASYNC, Execution.WITHOUT_ORIGIN),
625                     URL_FIRST, servlets_, servlets_);
626             verify(() -> extractLog(driver), String.join("\n", getExpectedAlerts()));
627         }
628 
629         /**
630          * @throws Exception if the test fails
631          */
632         @Test
633         @Alerts({"readystatechange_1_0_true", "open-done: 1_0",
634                  "loadstart_1_0_false", "send-done: 1_0", "readystatechange_4_0_true",
635                  "error_4_0_false", "loadend_4_0_false"})
636         public void addEventListener_async_preflight_without_origin() throws Exception {
637             final WebDriver driver = loadPage2(buildHtml(Mode.ASYNC, Execution.WITHOUT_ORIGIN_PREFLIGHT),
638                     URL_FIRST, servlets_, servlets_);
639             verify(() -> extractLog(driver), String.join("\n", getExpectedAlerts()));
640         }
641 
642         /**
643          * @throws Exception if the test fails
644          */
645         @Test
646         @Alerts({"readystatechange_1_0_true", "open-done: 1_0", "loadstart_1_0_false",
647                  "send-done: 1_0", "readystatechange_4_0_true", "abort_4_0_false",
648                  "loadend_4_0_false", "abort-done: 0_0"})
649         public void addEventListener_async_abortTriggered() throws Exception {
650             final WebDriver driver = loadPage2(buildHtml(Mode.ASYNC, Execution.SEND_ABORT), URL_FIRST,
651                     servlets_);
652             verify(() -> extractLog(driver), String.join("\n", getExpectedAlerts()));
653         }
654 
655         /**
656          * @throws Exception if the test fails
657          */
658         @Test
659         @Alerts(DEFAULT = {"readystatechange_1_0_true", "open-done: 1_0", "loadstart_1_0_false",
660                            "send-done: 1_0", "readystatechange_2_200_true", "readystatechange_3_200_true",
661                            "progress_3_200_false", "readystatechange_4_200_true",
662                            "abort-done: 0_0"},
663                 FF = {"readystatechange_1_0_true", "open-done: 1_0", "loadstart_1_0_false",
664                       "send-done: 1_0", "readystatechange_2_200_true", "readystatechange_3_200_true",
665                       "progress_3_200_false", "readystatechange_4_200_true",
666                       "abort-done: 0_0", "load_0_0_false", "loadend_0_0_false"},
667                 FF_ESR = {"readystatechange_1_0_true", "open-done: 1_0", "loadstart_1_0_false",
668                           "send-done: 1_0", "readystatechange_2_200_true", "readystatechange_3_200_true",
669                           "progress_3_200_false", "readystatechange_4_200_true",
670                           "abort-done: 0_0", "load_0_0_false", "loadend_0_0_false"})
671         public void addEventListener_async_abortAfterDoneTriggered() throws Exception {
672             final WebDriver driver = loadPage2(buildHtml(Mode.ASYNC, Execution.DONE_ABORT), URL_FIRST,
673                     servlets_);
674             verify(() -> extractLog(driver), String.join("\n", getExpectedAlerts()));
675         }
676 
677         /**
678          * @throws Exception if the test fails
679          */
680         @Test
681         @Alerts({"readystatechange_1_0_true", "open-done: 1_0", "loadstart_1_0_false",
682                  "send-done: 1_0", "readystatechange_4_0_true", "error_4_0_false",
683                  "loadend_4_0_false"})
684         public void addEventListener_async_networkErrorTriggered() throws Exception {
685             final WebDriver driver = loadPage2(buildHtml(Mode.ASYNC, Execution.NETWORK_ERROR), URL_FIRST,
686                     servlets_);
687             verify(() -> extractLog(driver), String.join("\n", getExpectedAlerts()));
688         }
689 
690         /**
691          * @throws Exception if the test fails
692          */
693         @Test
694         @Alerts({"readystatechange_1_0_true", "open-done: 1_0", "loadstart_1_0_false",
695                  "send-done: 1_0", "readystatechange_4_0_true", "error_4_0_false",
696                  "loadend_4_0_false"})
697         public void addEventListener_async_networkErrorTriggered_preflight() throws Exception {
698             final WebDriver driver = loadPage2(buildHtml(Mode.ASYNC, Execution.NETWORK_ERROR_PREFLIGHT), URL_FIRST,
699                     servlets_, servlets_);
700             verify(() -> extractLog(driver), String.join("\n", getExpectedAlerts()),
701                     DEFAULT_WAIT_TIME.multipliedBy(10));
702         }
703 
704         /**
705          * Error 500 on the server side still count as a valid requests for {@link XMLHttpRequest}.
706          * @throws Exception if the test fails
707          */
708         @Test
709         @Alerts({"readystatechange_1_0_true", "open-done: 1_0", "loadstart_1_0_false",
710                  "send-done: 1_0", "readystatechange_2_500_true", "readystatechange_3_500_true",
711                  "progress_3_500_false", "readystatechange_4_500_true",
712                  "load_4_500_false", "loadend_4_500_false"})
713         public void addEventListener_async_Error500Triggered() throws Exception {
714             final WebDriver driver = loadPage2(buildHtml(Mode.ASYNC, Execution.ERROR_500), URL_FIRST,
715                     servlets_);
716             verify(() -> extractLog(driver), String.join("\n", getExpectedAlerts()));
717         }
718 
719         /**
720          * Error 500 on the server side still count as a valid requests for {@link XMLHttpRequest}.
721          * @throws Exception if the test fails
722          */
723         @Test
724         @Alerts({"readystatechange_1_0_true", "open-done: 1_0", "loadstart_1_0_false",
725                  "send-done: 1_0", "readystatechange_2_500_true", "readystatechange_3_500_true",
726                  "progress_3_500_false", "readystatechange_4_500_true",
727                  "load_4_500_false", "loadend_4_500_false"})
728         public void addEventListener_async_Error500Triggered_preflight() throws Exception {
729             final WebDriver driver = loadPage2(buildHtml(Mode.ASYNC, Execution.ERROR_500_PREFLIGHT), URL_FIRST,
730                     servlets_, servlets_);
731             verify(() -> extractLog(driver), String.join("\n", getExpectedAlerts()));
732         }
733 
734         /**
735          * Error 500 on the server side still count as a valid requests for {@link XMLHttpRequest}.
736          * @throws Exception if the test fails
737          */
738         @Test
739         @Alerts({"readystatechange_1_0_true", "open-done: 1_0", "loadstart_1_0_false",
740                  "send-done: 1_0", "readystatechange_4_0_true",
741                  "error_4_0_false", "loadend_4_0_false"})
742         public void addEventListener_async_Error500Triggered_during_preflight() throws Exception {
743             final WebDriver driver = loadPage2(buildHtml(Mode.ASYNC, Execution.ERROR_500_DURING_PREFLIGHT), URL_FIRST,
744                     servlets_, servlets_);
745             verify(() -> extractLog(driver), String.join("\n", getExpectedAlerts()));
746         }
747 
748         /**
749          * @throws Exception if the test fails
750          */
751         @Test
752         @Alerts({"readystatechange_1_0_true", "open-done: 1_0", "loadstart_1_0_false",
753                  "send-done: 1_0", "readystatechange_4_0_true", "timeout_4_0_false",
754                  "loadend_4_0_false"})
755         public void addEventListener_async_timeout() throws Exception {
756             final WebDriver driver = loadPage2(buildHtml(Mode.ASYNC, Execution.TIMEOUT), URL_FIRST,
757                     servlets_);
758             verify(() -> extractLog(driver), String.join("\n", getExpectedAlerts()));
759         }
760 
761         // same tests as above, but this time we're triggering with the onkeyword.
762         /**
763          * @throws Exception if the test fails
764          */
765         @Test
766         @Alerts({"readystatechange_1_0_true", "open-done: 1_0", "readystatechange_4_200_true",
767                  "load_4_200_false", "loadend_4_200_false", "send-done: 4_200"})
768         public void onKeyWord_sync() throws Exception {
769             // we can register ourselves for every state here since it's in sync mode and
770             // most of them won't fire anyway.
771             final WebDriver driver = loadPage2(buildHtml(Mode.SYNC_ON_KEYWORD, Execution.ONLY_SEND),
772                     URL_FIRST, servlets_);
773             verify(() -> extractLog(driver), String.join("\n", getExpectedAlerts()));
774         }
775 
776         /**
777          * @throws Exception if the test fails
778          */
779         @Test
780         @Alerts({"readystatechange_1_0_true", "open-done: 1_0",
781                  "readystatechange_4_200_true", "load_4_200_false",
782                  "loadend_4_200_false", "send-done: 4_200"})
783         public void onKeyWord_sync_preflight() throws Exception {
784             final WebDriver driver = loadPage2(buildHtml(Mode.SYNC_ON_KEYWORD, Execution.ONLY_SEND_PREFLIGHT),
785                     URL_FIRST, servlets_, servlets_);
786             verify(() -> extractLog(driver), String.join("\n", getExpectedAlerts()));
787         }
788 
789         /**
790          * @throws Exception if the test fails
791          */
792         @Test
793         @Alerts(DEFAULT = {"readystatechange_1_0_true", "open-done: 1_0",
794                            "ExceptionThrown"},
795                 FF = {"readystatechange_1_0_true", "open-done: 1_0",
796                       "readystatechange_4_0_true", "error_4_0_false",
797                       "loadend_4_0_false", "ExceptionThrown"},
798                 FF_ESR = {"readystatechange_1_0_true", "open-done: 1_0",
799                           "readystatechange_4_0_true", "error_4_0_false",
800                           "loadend_4_0_false", "ExceptionThrown"})
801         public void onKeyWord_sync_preflight_forbidden() throws Exception {
802             final WebDriver driver = loadPage2(buildHtml(Mode.SYNC_ON_KEYWORD, Execution.ONLY_SEND_PREFLIGHT_FORBIDDEN),
803                     URL_FIRST, servlets_, servlets_);
804             verify(() -> extractLog(driver), String.join("\n", getExpectedAlerts()));
805         }
806 
807         /**
808          * @throws Exception if the test fails
809          */
810         @Test
811         @Alerts(DEFAULT = {"readystatechange_1_0_true", "open-done: 1_0", "ExceptionThrown"},
812                 FF = {"readystatechange_1_0_true", "open-done: 1_0",
813                       "readystatechange_4_0_true", "error_4_0_false", "loadend_4_0_false",
814                       "ExceptionThrown"},
815                 FF_ESR = {"readystatechange_1_0_true", "open-done: 1_0",
816                           "readystatechange_4_0_true", "error_4_0_false", "loadend_4_0_false",
817                           "ExceptionThrown"})
818         public void onKeyWord_sync_without_origin() throws Exception {
819             final WebDriver driver = loadPage2(buildHtml(Mode.SYNC_ON_KEYWORD, Execution.WITHOUT_ORIGIN),
820                     URL_FIRST, servlets_, servlets_);
821             verify(() -> extractLog(driver), String.join("\n", getExpectedAlerts()));
822         }
823 
824         /**
825          * @throws Exception if the test fails
826          */
827         @Test
828         @Alerts(DEFAULT = {"readystatechange_1_0_true", "open-done: 1_0", "ExceptionThrown"},
829                 FF = {"readystatechange_1_0_true", "open-done: 1_0",
830                       "readystatechange_4_0_true", "error_4_0_false", "loadend_4_0_false",
831                       "ExceptionThrown"},
832                 FF_ESR = {"readystatechange_1_0_true", "open-done: 1_0",
833                           "readystatechange_4_0_true", "error_4_0_false", "loadend_4_0_false",
834                           "ExceptionThrown"})
835         public void onKeyWord_sync_preflight_without_origin() throws Exception {
836             final WebDriver driver = loadPage2(buildHtml(Mode.SYNC_ON_KEYWORD, Execution.WITHOUT_ORIGIN_PREFLIGHT),
837                     URL_FIRST, servlets_, servlets_);
838             verify(() -> extractLog(driver), String.join("\n", getExpectedAlerts()));
839         }
840 
841 
842         /**
843          * @throws Exception if the test fails
844          */
845         @Test
846         @Alerts({"readystatechange_1_0_true", "open-done: 1_0", "readystatechange_4_200_true",
847                  "load_4_200_false", "loadend_4_200_false", "send-done: 4_200", "abort-done: 0_0"})
848         public void onKeyWord_sync_abortTriggered() throws Exception {
849             final WebDriver driver = loadPage2(buildHtml(Mode.SYNC_ON_KEYWORD, Execution.SEND_ABORT),
850                     URL_FIRST, servlets_);
851             verify(() -> extractLog(driver), String.join("\n", getExpectedAlerts()));
852         }
853 
854         /**
855          * @throws Exception if the test fails
856          */
857         @Test
858         @Alerts(DEFAULT = {"readystatechange_1_0_true", "open-done: 1_0", "readystatechange_4_200_true",
859                            "abort-done: 0_0", "ExceptionThrown"},
860                 FF = {"readystatechange_1_0_true", "open-done: 1_0", "readystatechange_4_200_true",
861                       "abort-done: 0_0", "load_0_0_false", "loadend_0_0_false", "send-done: 0_0"},
862                 FF_ESR = {"readystatechange_1_0_true", "open-done: 1_0", "readystatechange_4_200_true",
863                           "abort-done: 0_0", "load_0_0_false", "loadend_0_0_false", "send-done: 0_0"})
864         public void onKeyWord_sync_abortAfterDoneTriggered() throws Exception {
865             final WebDriver driver = loadPage2(buildHtml(Mode.SYNC_ON_KEYWORD, Execution.DONE_ABORT),
866                     URL_FIRST, servlets_);
867             verify(() -> extractLog(driver), String.join("\n", getExpectedAlerts()));
868         }
869 
870         /**
871          * @throws Exception if the test fails
872          */
873         @Test
874         @Alerts(DEFAULT = {"readystatechange_1_0_true", "open-done: 1_0", "ExceptionThrown"},
875                 FF = {"readystatechange_1_0_true", "open-done: 1_0", "readystatechange_4_0_true",
876                       "error_4_0_false", "loadend_4_0_false", "ExceptionThrown"},
877                 FF_ESR = {"readystatechange_1_0_true", "open-done: 1_0", "readystatechange_4_0_true",
878                           "error_4_0_false", "loadend_4_0_false", "ExceptionThrown"})
879         public void onKeyWord_sync_networkError() throws Exception {
880             // will throw an exception and user is supposed to handle this.
881             // That's why we only have one readystatechange callback.
882             try {
883                 loadPage2(buildHtml(Mode.SYNC_ON_KEYWORD, Execution.NETWORK_ERROR), URL_FIRST, servlets_);
884 
885             }
886             catch (final WebDriverException e) {
887                 if (useRealBrowser()) {
888                     // we only expect the error to be thrown in htmlunit scenarios.
889                     throw e;
890                 }
891             }
892             finally {
893                 verify(() -> extractLog(getWebDriver()), String.join("\n", getExpectedAlerts()));
894             }
895         }
896 
897         /**
898          * @throws Exception if the test fails
899          */
900         @Test
901         @Alerts({"readystatechange_1_0_true", "open-done: 1_0", "readystatechange_4_403_true",
902                  "load_4_403_false", "loadend_4_403_false", "send-done: 4_403"})
903         public void onKeyWord_sync_Error403() throws Exception {
904             final WebDriver driver = loadPage2(buildHtml(Mode.SYNC_ON_KEYWORD, Execution.ERROR_403), URL_FIRST,
905                     servlets_);
906             verify(() -> extractLog(driver), String.join("\n", getExpectedAlerts()),
907                     DEFAULT_WAIT_TIME.multipliedBy(10));
908         }
909 
910         /**
911          * @throws Exception if the test fails
912          */
913         @Test
914         @Alerts({"readystatechange_1_0_true", "open-done: 1_0", "readystatechange_4_403_true",
915                  "load_4_403_false", "loadend_4_403_false", "send-done: 4_403"})
916         public void onKeyWord_sync_Error403_preflight() throws Exception {
917             final WebDriver driver = loadPage2(buildHtml(Mode.SYNC_ON_KEYWORD,
918                     Execution.ERROR_403_PREFLIGHT), URL_FIRST,
919                     servlets_, servlets_);
920             verify(() -> extractLog(driver), String.join("\n", getExpectedAlerts()));
921         }
922 
923         /**
924          * @throws Exception if the test fails
925          */
926         @Test
927         @Alerts(DEFAULT = {"readystatechange_1_0_true", "open-done: 1_0", "ExceptionThrown"},
928                 FF = {"readystatechange_1_0_true", "open-done: 1_0",
929                       "readystatechange_4_0_true", "error_4_0_false", "loadend_4_0_false",
930                       "ExceptionThrown"},
931                 FF_ESR = {"readystatechange_1_0_true", "open-done: 1_0",
932                           "readystatechange_4_0_true", "error_4_0_false", "loadend_4_0_false",
933                           "ExceptionThrown"})
934         public void onKeyWord_sync_Error403_during_preflight() throws Exception {
935             final WebDriver driver = loadPage2(buildHtml(Mode.SYNC_ON_KEYWORD,
936                     Execution.ERROR_403_DURING_PREFLIGHT), URL_FIRST,
937                     servlets_, servlets_);
938             verify(() -> extractLog(driver), String.join("\n", getExpectedAlerts()));
939         }
940 
941         /**
942          * @throws Exception if the test fails
943          */
944         @Test
945         @Alerts({"readystatechange_1_0_true", "open-done: 1_0", "readystatechange_4_500_true",
946                  "load_4_500_false", "loadend_4_500_false", "send-done: 4_500"})
947         public void onKeyWord_sync_Error500() throws Exception {
948             final WebDriver driver = loadPage2(buildHtml(Mode.SYNC_ON_KEYWORD, Execution.ERROR_500), URL_FIRST,
949                     servlets_);
950             verify(() -> extractLog(driver), String.join("\n", getExpectedAlerts()),
951                     DEFAULT_WAIT_TIME.multipliedBy(10));
952         }
953 
954         /**
955          * @throws Exception if the test fails
956          */
957         @Test
958         @Alerts({"readystatechange_1_0_true", "open-done: 1_0", "readystatechange_4_500_true",
959                  "load_4_500_false", "loadend_4_500_false", "send-done: 4_500"})
960         public void onKeyWord_sync_Error500_preflight() throws Exception {
961             final WebDriver driver = loadPage2(buildHtml(Mode.SYNC_ON_KEYWORD,
962                     Execution.ERROR_500_PREFLIGHT), URL_FIRST,
963                     servlets_, servlets_);
964             verify(() -> extractLog(driver), String.join("\n", getExpectedAlerts()));
965         }
966 
967         /**
968          * @throws Exception if the test fails
969          */
970         @Test
971         @Alerts(DEFAULT = {"readystatechange_1_0_true", "open-done: 1_0", "ExceptionThrown"},
972                 FF = {"readystatechange_1_0_true", "open-done: 1_0",
973                       "readystatechange_4_0_true", "error_4_0_false", "loadend_4_0_false",
974                       "ExceptionThrown"},
975                 FF_ESR = {"readystatechange_1_0_true", "open-done: 1_0",
976                           "readystatechange_4_0_true", "error_4_0_false", "loadend_4_0_false",
977                           "ExceptionThrown"})
978         public void onKeyWord_sync_Error500_during_preflight() throws Exception {
979             final WebDriver driver = loadPage2(buildHtml(Mode.SYNC_ON_KEYWORD,
980                     Execution.ERROR_500_DURING_PREFLIGHT), URL_FIRST,
981                     servlets_, servlets_);
982             verify(() -> extractLog(driver), String.join("\n", getExpectedAlerts()));
983         }
984 
985         /**
986          * @throws Exception if the test fails
987          */
988         @Test
989         @Alerts({"readystatechange_1_0_true", "open-done: 1_0", "ExceptionThrown"})
990         public void onKeyWord_sync_timeout() throws Exception {
991             final WebDriver driver = loadPage2(buildHtml(Mode.SYNC_ON_KEYWORD, Execution.TIMEOUT),
992                     URL_FIRST, servlets_);
993             verify(() -> extractLog(driver), String.join("\n", getExpectedAlerts()));
994         }
995 
996         /**
997          * @throws Exception if the test fails
998          */
999         @Test
1000         @Alerts({"readystatechange_1_0_true", "open-done: 1_0", "loadstart_1_0_false",
1001                  "send-done: 1_0", "readystatechange_2_200_true", "readystatechange_3_200_true",
1002                  "progress_3_200_false", "readystatechange_4_200_true", "load_4_200_false",
1003                  "loadend_4_200_false"})
1004         public void onKeyWord_async() throws Exception {
1005             final WebDriver driver = loadPage2(buildHtml(Mode.ASYNC_ON_KEYWORD, Execution.ONLY_SEND),
1006                     URL_FIRST, servlets_);
1007             verify(() -> extractLog(driver), String.join("\n", getExpectedAlerts()));
1008         }
1009 
1010         /**
1011          * @throws Exception if the test fails
1012          */
1013         @Test
1014         @Alerts({"readystatechange_1_0_true", "open-done: 1_0", "loadstart_1_0_false",
1015                  "send-done: 1_0", "readystatechange_2_200_true", "readystatechange_3_200_true",
1016                  "progress_3_200_false", "readystatechange_4_200_true",
1017                  "load_4_200_false", "loadend_4_200_false"})
1018         public void onKeyWord_async_preflight() throws Exception {
1019             final WebDriver driver = loadPage2(buildHtml(Mode.ASYNC_ON_KEYWORD, Execution.ONLY_SEND_PREFLIGHT),
1020                     URL_FIRST, servlets_, servlets_);
1021             verify(() -> extractLog(driver), String.join("\n", getExpectedAlerts()));
1022         }
1023 
1024         /**
1025          * @throws Exception if the test fails
1026          */
1027         @Test
1028         @Retry
1029         @Alerts({"readystatechange_1_0_true", "open-done: 1_0", "loadstart_1_0_false",
1030                  "send-done: 1_0", "readystatechange_4_0_true", "abort_4_0_false",
1031                  "loadend_4_0_false", "abort-done: 0_0"})
1032         public void onKeyWord_async_abortTriggered() throws Exception {
1033             final WebDriver driver = loadPage2(buildHtml(Mode.ASYNC_ON_KEYWORD, Execution.SEND_ABORT),
1034                     URL_FIRST, servlets_);
1035             verify(() -> extractLog(driver), String.join("\n", getExpectedAlerts()));
1036         }
1037 
1038         /**
1039          * @throws Exception if the test fails
1040          */
1041         @Test
1042         @Alerts(DEFAULT = {"readystatechange_1_0_true", "open-done: 1_0", "loadstart_1_0_false",
1043                            "send-done: 1_0", "readystatechange_2_200_true", "readystatechange_3_200_true",
1044                            "progress_3_200_false", "readystatechange_4_200_true", "abort-done: 0_0"},
1045                 FF = {"readystatechange_1_0_true", "open-done: 1_0", "loadstart_1_0_false",
1046                       "send-done: 1_0", "readystatechange_2_200_true", "readystatechange_3_200_true",
1047                       "progress_3_200_false", "readystatechange_4_200_true", "abort-done: 0_0",
1048                       "load_0_0_false", "loadend_0_0_false"},
1049                 FF_ESR = {"readystatechange_1_0_true", "open-done: 1_0", "loadstart_1_0_false",
1050                           "send-done: 1_0", "readystatechange_2_200_true", "readystatechange_3_200_true",
1051                           "progress_3_200_false", "readystatechange_4_200_true", "abort-done: 0_0",
1052                           "load_0_0_false", "loadend_0_0_false"})
1053         public void onKeyWord_async_abortAfterDoneTriggered() throws Exception {
1054             final WebDriver driver = loadPage2(buildHtml(Mode.ASYNC_ON_KEYWORD, Execution.DONE_ABORT),
1055                     URL_FIRST, servlets_);
1056             verify(() -> extractLog(driver), String.join("\n", getExpectedAlerts()));
1057         }
1058 
1059         /**
1060          * @throws Exception if the test fails
1061          */
1062         @Test
1063         @Alerts({"readystatechange_1_0_true", "open-done: 1_0", "loadstart_1_0_false",
1064                  "send-done: 1_0", "readystatechange_4_0_true", "error_4_0_false",
1065                  "loadend_4_0_false"})
1066         public void onKeyWord_async_networkErrorTriggered() throws Exception {
1067             final WebDriver driver = loadPage2(buildHtml(Mode.ASYNC_ON_KEYWORD, Execution.NETWORK_ERROR),
1068                     URL_FIRST, servlets_);
1069             verify(() -> extractLog(driver), String.join("\n", getExpectedAlerts()));
1070         }
1071 
1072 
1073         /**
1074          * Error 403 on the server side still count as a valid requests for {@link XMLHttpRequest}.
1075          * @throws Exception if the test fails
1076          */
1077         @Test
1078         @Alerts({"readystatechange_1_0_true", "open-done: 1_0", "loadstart_1_0_false",
1079                  "send-done: 1_0", "readystatechange_2_403_true", "readystatechange_3_403_true",
1080                  "progress_3_403_false", "readystatechange_4_403_true",
1081                  "load_4_403_false", "loadend_4_403_false"})
1082         public void onKeyWord_async_Error403Triggered() throws Exception {
1083             final WebDriver driver = loadPage2(buildHtml(Mode.ASYNC_ON_KEYWORD, Execution.ERROR_403), URL_FIRST,
1084                     servlets_);
1085             verify(() -> extractLog(driver), String.join("\n", getExpectedAlerts()));
1086         }
1087 
1088         /**
1089          * Error 403 on the server side still count as a valid requests for {@link XMLHttpRequest}.
1090          * @throws Exception if the test fails
1091          */
1092         @Test
1093         @Alerts({"readystatechange_1_0_true", "open-done: 1_0", "loadstart_1_0_false",
1094                  "send-done: 1_0", "readystatechange_2_403_true", "readystatechange_3_403_true",
1095                  "progress_3_403_false", "readystatechange_4_403_true",
1096                  "load_4_403_false", "loadend_4_403_false"})
1097         public void onKeyWord_async_Error403Triggered_preflight() throws Exception {
1098             final WebDriver driver = loadPage2(buildHtml(Mode.ASYNC_ON_KEYWORD,
1099                     Execution.ERROR_403_PREFLIGHT), URL_FIRST,
1100                     servlets_, servlets_);
1101             verify(() -> extractLog(driver), String.join("\n", getExpectedAlerts()));
1102         }
1103 
1104         /**
1105          * Error 403 on the server side still count as a valid requests for {@link XMLHttpRequest}.
1106          * @throws Exception if the test fails
1107          */
1108         @Test
1109         @Alerts({"readystatechange_1_0_true", "open-done: 1_0", "loadstart_1_0_false",
1110                  "send-done: 1_0", "readystatechange_4_0_true",
1111                  "error_4_0_false", "loadend_4_0_false"})
1112         public void onKeyWord_async_Error403Triggered_during_preflight() throws Exception {
1113             final WebDriver driver = loadPage2(buildHtml(Mode.ASYNC_ON_KEYWORD,
1114                     Execution.ERROR_403_DURING_PREFLIGHT), URL_FIRST,
1115                     servlets_, servlets_);
1116             verify(() -> extractLog(driver), String.join("\n", getExpectedAlerts()));
1117         }
1118 
1119         /**
1120          * Error 500 on the server side still count as a valid requests for {@link XMLHttpRequest}.
1121          * @throws Exception if the test fails
1122          */
1123         @Test
1124         @Alerts({"readystatechange_1_0_true", "open-done: 1_0", "loadstart_1_0_false",
1125                  "send-done: 1_0", "readystatechange_2_500_true", "readystatechange_3_500_true",
1126                  "progress_3_500_false", "readystatechange_4_500_true", "load_4_500_false",
1127                  "loadend_4_500_false"})
1128         public void onKeyWord_async_Error500Triggered() throws Exception {
1129             final WebDriver driver = loadPage2(buildHtml(Mode.ASYNC_ON_KEYWORD, Execution.ERROR_500),
1130                     URL_FIRST, servlets_);
1131             verify(() -> extractLog(driver), String.join("\n", getExpectedAlerts()));
1132         }
1133 
1134         /**
1135          * Error 403 on the server side still count as a valid requests for {@link XMLHttpRequest}.
1136          * @throws Exception if the test fails
1137          */
1138         @Test
1139         @Alerts({"readystatechange_1_0_true", "open-done: 1_0", "loadstart_1_0_false",
1140                  "send-done: 1_0", "readystatechange_2_403_true", "readystatechange_3_403_true",
1141                  "progress_3_403_false", "readystatechange_4_403_true",
1142                  "load_4_403_false", "loadend_4_403_false"})
1143         public void addEventListener_async_Error403Triggered() throws Exception {
1144             final WebDriver driver = loadPage2(buildHtml(Mode.ASYNC, Execution.ERROR_403), URL_FIRST,
1145                     servlets_);
1146             verify(() -> extractLog(driver), String.join("\n", getExpectedAlerts()));
1147         }
1148 
1149         /**
1150          * Error 403 on the server side still count as a valid requests for {@link XMLHttpRequest}.
1151          * @throws Exception if the test fails
1152          */
1153         @Test
1154         @Alerts({"readystatechange_1_0_true", "open-done: 1_0", "loadstart_1_0_false",
1155                  "send-done: 1_0", "readystatechange_2_403_true", "readystatechange_3_403_true",
1156                  "progress_3_403_false", "readystatechange_4_403_true",
1157                  "load_4_403_false", "loadend_4_403_false"})
1158         public void addEventListener_async_Error403Triggered_preflight() throws Exception {
1159             final WebDriver driver = loadPage2(buildHtml(Mode.ASYNC, Execution.ERROR_403_PREFLIGHT), URL_FIRST,
1160                     servlets_, servlets_);
1161             verify(() -> extractLog(driver), String.join("\n", getExpectedAlerts()));
1162         }
1163 
1164         /**
1165          * Error 403 on the server side still count as a valid requests for {@link XMLHttpRequest}.
1166          * @throws Exception if the test fails
1167          */
1168         @Test
1169         @Alerts({"readystatechange_1_0_true", "open-done: 1_0", "loadstart_1_0_false",
1170                  "send-done: 1_0", "readystatechange_4_0_true",
1171                  "error_4_0_false", "loadend_4_0_false"})
1172         public void addEventListener_async_Error403Triggered_during_preflight() throws Exception {
1173             final WebDriver driver = loadPage2(buildHtml(Mode.ASYNC, Execution.ERROR_403_DURING_PREFLIGHT), URL_FIRST,
1174                     servlets_, servlets_);
1175             verify(() -> extractLog(driver), String.join("\n", getExpectedAlerts()));
1176         }
1177 
1178         /**
1179          * Error 500 on the server side still count as a valid requests for {@link XMLHttpRequest}.
1180          * @throws Exception if the test fails
1181          */
1182         @Test
1183         @Alerts({"readystatechange_1_0_true", "open-done: 1_0", "loadstart_1_0_false",
1184                  "send-done: 1_0", "readystatechange_2_500_true", "readystatechange_3_500_true",
1185                  "progress_3_500_false", "readystatechange_4_500_true",
1186                  "load_4_500_false", "loadend_4_500_false"})
1187         public void onKeyWord_async_Error500Triggered_preflight() throws Exception {
1188             final WebDriver driver = loadPage2(buildHtml(Mode.ASYNC_ON_KEYWORD,
1189                     Execution.ERROR_500_PREFLIGHT), URL_FIRST,
1190                     servlets_, servlets_);
1191             verify(() -> extractLog(driver), String.join("\n", getExpectedAlerts()));
1192         }
1193 
1194         /**
1195          * Error 500 on the server side still count as a valid requests for {@link XMLHttpRequest}.
1196          * @throws Exception if the test fails
1197          */
1198         @Test
1199         @Alerts({"readystatechange_1_0_true", "open-done: 1_0", "loadstart_1_0_false",
1200                  "send-done: 1_0", "readystatechange_4_0_true",
1201                  "error_4_0_false", "loadend_4_0_false"})
1202         public void onKeyWord_async_Error500Triggered_during_preflight() throws Exception {
1203             final WebDriver driver = loadPage2(buildHtml(Mode.ASYNC_ON_KEYWORD,
1204                     Execution.ERROR_500_DURING_PREFLIGHT), URL_FIRST,
1205                     servlets_, servlets_);
1206             verify(() -> extractLog(driver), String.join("\n", getExpectedAlerts()));
1207         }
1208 
1209         /**
1210          * @throws Exception if the test fails
1211          */
1212         @Test
1213         @Alerts({"readystatechange_1_0_true", "open-done: 1_0", "loadstart_1_0_false",
1214                  "send-done: 1_0", "readystatechange_4_0_true", "timeout_4_0_false",
1215                  "loadend_4_0_false"})
1216         public void onKeyWord_async_timeout() throws Exception {
1217             final WebDriver driver = loadPage2(buildHtml(Mode.ASYNC_ON_KEYWORD, Execution.TIMEOUT),
1218                     URL_FIRST, servlets_);
1219             verify(() -> extractLog(driver), String.join("\n", getExpectedAlerts()));
1220         }
1221     }
1222 
1223     /**
1224      * Test using our MiniServer to be able to simulate special error conditions.
1225      */
1226     @RunWith(BrowserRunner.class)
1227     public static class MiniServerTest extends WebDriverTestCase {
1228 
1229         /**
1230          * Shoutdown all web browsers and reset the {@link MiniServer}.
1231          * @throws Exception in case of error
1232          */
1233         @Before
1234         public void before() throws Exception {
1235             // Chrome seems to cache preflight results
1236             shutDownAll();
1237             MiniServer.resetDropRequests();
1238         }
1239 
1240         /**
1241          * Reset the {@link MiniServer}.
1242          * @throws Exception in case of error.
1243          */
1244         @After
1245         public void after() throws Exception {
1246             MiniServer.resetDropRequests();
1247         }
1248 
1249         /**
1250          * NoHttpResponseException.
1251          * @throws Exception if the test fails
1252          */
1253         @Test
1254         @Alerts(DEFAULT = {"readystatechange_1_0_true", "open-done: 1_0", "ExceptionThrown"},
1255                 FF = {"readystatechange_1_0_true", "open-done: 1_0", "readystatechange_4_0_true",
1256                       "error_4_0_false", "loadend_4_0_false", "ExceptionThrown"},
1257                 FF_ESR = {"readystatechange_1_0_true", "open-done: 1_0", "readystatechange_4_0_true",
1258                           "error_4_0_false", "loadend_4_0_false", "ExceptionThrown"})
1259         public void addEventListener_sync_NoHttpResponseException() throws Exception {
1260             final MockWebConnection mockWebConnection = getMockWebConnection();
1261             mockWebConnection.setResponse(WebTestCase.URL_FIRST, buildHtml(Mode.SYNC, Execution.ONLY_SEND));
1262             MiniServer.configureDropRequest(new URL(WebTestCase.URL_FIRST + SUCCESS_URL));
1263 
1264             try (MiniServer miniServer = new MiniServer(PORT, mockWebConnection)) {
1265                 miniServer.start();
1266 
1267                 final WebDriver driver = getWebDriver();
1268                 driver.get(WebTestCase.URL_FIRST.toExternalForm());
1269 
1270                 verify(() -> extractLog(driver), String.join("\n", getExpectedAlerts()));
1271 
1272                 // no chance to to check the request count because of retries
1273                 assertEquals(new URL(WebTestCase.URL_FIRST, SUCCESS_URL),
1274                         mockWebConnection.getLastWebRequest().getUrl());
1275                 assertEquals(HttpMethod.GET,
1276                         mockWebConnection.getLastWebRequest().getHttpMethod());
1277             }
1278         }
1279 
1280         /**
1281          * NoHttpResponseException after preflight.
1282          * @throws Exception if the test fails
1283          */
1284         @Test
1285         @Alerts(DEFAULT = {"readystatechange_1_0_true", "open-done: 1_0", "ExceptionThrown"},
1286                 FF = {"readystatechange_1_0_true", "open-done: 1_0", "readystatechange_4_0_true",
1287                       "error_4_0_false", "loadend_4_0_false", "ExceptionThrown"},
1288                 FF_ESR = {"readystatechange_1_0_true", "open-done: 1_0", "readystatechange_4_0_true",
1289                           "error_4_0_false", "loadend_4_0_false", "ExceptionThrown"})
1290         public void addEventListener_sync_preflight_NoHttpResponseException() throws Exception {
1291             final MockWebConnection mockWebConnection = getMockWebConnection();
1292             mockWebConnection.setResponse(WebTestCase.URL_FIRST, buildHtml(Mode.SYNC, Execution.ONLY_SEND_PREFLIGHT));
1293 
1294             final List<NameValuePair> headers = new ArrayList<>();
1295             headers.add(new NameValuePair("Access-Control-Allow-Origin", "*"));
1296             headers.add(new NameValuePair("Access-Control-Allow-Methods", "*"));
1297             headers.add(new NameValuePair("Access-Control-Allow-Headers", "X-PINGOTHER"));
1298             getMockWebConnection().setResponse(new URL("http://localhost:" + PORT2 + SUCCESS_URL),
1299                     "",  200, "OK", MimeType.TEXT_HTML, headers);
1300             MiniServer.configureDropGetRequest(new URL("http://localhost:" + PORT2 + SUCCESS_URL));
1301 
1302             try (MiniServer miniServer1 = new MiniServer(PORT, mockWebConnection)) {
1303                 miniServer1.start();
1304 
1305                 try (MiniServer miniServer2 = new MiniServer(PORT2, mockWebConnection)) {
1306                     miniServer2.start();
1307 
1308                     final WebDriver driver = getWebDriver();
1309                     driver.get(WebTestCase.URL_FIRST.toExternalForm());
1310 
1311                     verify(() -> extractLog(driver), String.join("\n", getExpectedAlerts()));
1312 
1313                     // no chance to to check the request count because of retries
1314                     assertEquals(new URL("http://localhost:" + PORT2 + SUCCESS_URL),
1315                             mockWebConnection.getLastWebRequest().getUrl());
1316                     assertEquals(HttpMethod.GET,
1317                             mockWebConnection.getLastWebRequest().getHttpMethod());
1318                 }
1319             }
1320         }
1321 
1322         /**
1323          * NoHttpResponseException during preflight.
1324          * @throws Exception if the test fails
1325          */
1326         @Test
1327         @Alerts(DEFAULT = {"readystatechange_1_0_true", "open-done: 1_0", "ExceptionThrown"},
1328                 FF = {"readystatechange_1_0_true", "open-done: 1_0", "readystatechange_4_0_true",
1329                       "error_4_0_false", "loadend_4_0_false", "ExceptionThrown"},
1330                 FF_ESR = {"readystatechange_1_0_true", "open-done: 1_0", "readystatechange_4_0_true",
1331                           "error_4_0_false", "loadend_4_0_false", "ExceptionThrown"})
1332         public void addEventListener_sync_preflight_NoHttpResponseException_during_preflight() throws Exception {
1333             final MockWebConnection mockWebConnection = getMockWebConnection();
1334             mockWebConnection.setResponse(WebTestCase.URL_FIRST, buildHtml(Mode.SYNC, Execution.ONLY_SEND_PREFLIGHT));
1335             MiniServer.configureDropRequest(new URL("http://localhost:" + PORT2 + SUCCESS_URL));
1336 
1337             try (MiniServer miniServer1 = new MiniServer(PORT, mockWebConnection)) {
1338                 miniServer1.start();
1339 
1340                 try (MiniServer miniServer2 = new MiniServer(PORT2, mockWebConnection)) {
1341                     miniServer2.start();
1342 
1343                     final WebDriver driver = getWebDriver();
1344                     driver.get(WebTestCase.URL_FIRST.toExternalForm());
1345 
1346                     verify(() -> extractLog(driver), String.join("\n", getExpectedAlerts()));
1347 
1348                     // no chance to to check the request count because of retries
1349                     assertEquals(new URL("http://localhost:" + PORT2 + SUCCESS_URL),
1350                             mockWebConnection.getLastWebRequest().getUrl());
1351                     assertEquals(HttpMethod.OPTIONS,
1352                             mockWebConnection.getLastWebRequest().getHttpMethod());
1353                 }
1354             }
1355         }
1356 
1357         /**
1358          * NoHttpResponseException.
1359          * @throws Exception if the test fails
1360          */
1361         @Test
1362         @Alerts({"readystatechange_1_0_true", "open-done: 1_0", "loadstart_1_0_false",
1363                  "send-done: 1_0", "readystatechange_4_0_true", "error_4_0_false",
1364                  "loadend_4_0_false"})
1365         public void addEventListener_async_NoHttpResponseException() throws Exception {
1366             final MockWebConnection mockWebConnection = getMockWebConnection();
1367             mockWebConnection.setResponse(WebTestCase.URL_FIRST, buildHtml(Mode.ASYNC, Execution.ONLY_SEND));
1368             MiniServer.configureDropRequest(new URL(WebTestCase.URL_FIRST + SUCCESS_URL));
1369 
1370             try (MiniServer miniServer = new MiniServer(PORT, mockWebConnection)) {
1371                 miniServer.start();
1372 
1373                 final WebDriver driver = getWebDriver();
1374                 driver.get(WebTestCase.URL_FIRST.toExternalForm());
1375 
1376                 verify(() -> extractLog(driver), String.join("\n", getExpectedAlerts()));
1377 
1378                 // no chance to to check the request count because of retries
1379                 assertEquals(new URL(WebTestCase.URL_FIRST, SUCCESS_URL),
1380                         mockWebConnection.getLastWebRequest().getUrl());
1381                 assertEquals(HttpMethod.GET,
1382                         mockWebConnection.getLastWebRequest().getHttpMethod());
1383             }
1384         }
1385 
1386         /**
1387          * NoHttpResponseException after preflight.
1388          * @throws Exception if the test fails
1389          */
1390         @Test
1391         @Alerts({"readystatechange_1_0_true", "open-done: 1_0", "loadstart_1_0_false",
1392                  "send-done: 1_0", "readystatechange_4_0_true", "error_4_0_false",
1393                  "loadend_4_0_false"})
1394         public void addEventListener_async_preflight_NoHttpResponseException() throws Exception {
1395             final MockWebConnection mockWebConnection = getMockWebConnection();
1396             mockWebConnection.setResponse(WebTestCase.URL_FIRST, buildHtml(Mode.ASYNC, Execution.ONLY_SEND_PREFLIGHT));
1397 
1398             final List<NameValuePair> headers = new ArrayList<>();
1399             headers.add(new NameValuePair("Access-Control-Allow-Origin", "*"));
1400             headers.add(new NameValuePair("Access-Control-Allow-Methods", "*"));
1401             headers.add(new NameValuePair("Access-Control-Allow-Headers", "X-PINGOTHER"));
1402             getMockWebConnection().setResponse(new URL("http://localhost:" + PORT2 + SUCCESS_URL),
1403                     "",  200, "OK", MimeType.TEXT_HTML, headers);
1404             MiniServer.configureDropGetRequest(new URL("http://localhost:" + PORT2 + SUCCESS_URL));
1405 
1406             try (MiniServer miniServer1 = new MiniServer(PORT, mockWebConnection)) {
1407                 miniServer1.start();
1408 
1409                 try (MiniServer miniServer2 = new MiniServer(PORT2, mockWebConnection)) {
1410                     miniServer2.start();
1411 
1412                     final WebDriver driver = getWebDriver();
1413                     driver.get(WebTestCase.URL_FIRST.toExternalForm());
1414 
1415                     verify(() -> extractLog(driver), String.join("\n", getExpectedAlerts()));
1416 
1417                     // no chance to to check the request count because of retries
1418                     assertEquals(new URL("http://localhost:" + PORT2 + SUCCESS_URL),
1419                             mockWebConnection.getLastWebRequest().getUrl());
1420                     assertEquals(HttpMethod.GET,
1421                             mockWebConnection.getLastWebRequest().getHttpMethod());
1422                 }
1423             }
1424         }
1425 
1426         /**
1427          * NoHttpResponseException during preflight.
1428          * @throws Exception if the test fails
1429          */
1430         @Test
1431         @Alerts({"readystatechange_1_0_true", "open-done: 1_0", "loadstart_1_0_false",
1432                  "send-done: 1_0", "readystatechange_4_0_true", "error_4_0_false",
1433                  "loadend_4_0_false"})
1434         public void addEventListener_async_preflight_NoHttpResponseException_during_preflight() throws Exception {
1435             final MockWebConnection mockWebConnection = getMockWebConnection();
1436             mockWebConnection.setResponse(WebTestCase.URL_FIRST, buildHtml(Mode.ASYNC, Execution.ONLY_SEND_PREFLIGHT));
1437             MiniServer.configureDropRequest(new URL("http://localhost:" + PORT2 + SUCCESS_URL));
1438 
1439             try (MiniServer miniServer1 = new MiniServer(PORT, mockWebConnection)) {
1440                 miniServer1.start();
1441 
1442                 try (MiniServer miniServer2 = new MiniServer(PORT2, mockWebConnection)) {
1443                     miniServer2.start();
1444 
1445                     final WebDriver driver = getWebDriver();
1446                     driver.get(WebTestCase.URL_FIRST.toExternalForm());
1447 
1448                     verify(() -> extractLog(driver), String.join("\n", getExpectedAlerts()));
1449 
1450                     // no chance to to check the request count because of retries
1451                     assertEquals(new URL("http://localhost:" + PORT2 + SUCCESS_URL),
1452                             mockWebConnection.getLastWebRequest().getUrl());
1453                     assertEquals(HttpMethod.OPTIONS,
1454                             mockWebConnection.getLastWebRequest().getHttpMethod());
1455                 }
1456             }
1457         }
1458 
1459         /**
1460          * NoHttpResponseException.
1461          * @throws Exception if the test fails
1462          */
1463         @Test
1464         @Alerts(DEFAULT = {"readystatechange_1_0_true", "open-done: 1_0", "ExceptionThrown"},
1465                 FF = {"readystatechange_1_0_true", "open-done: 1_0", "readystatechange_4_0_true",
1466                       "error_4_0_false", "loadend_4_0_false", "ExceptionThrown"},
1467                 FF_ESR = {"readystatechange_1_0_true", "open-done: 1_0", "readystatechange_4_0_true",
1468                           "error_4_0_false", "loadend_4_0_false", "ExceptionThrown"})
1469         public void onKeyWord_sync_NoHttpResponseException() throws Exception {
1470             final MockWebConnection mockWebConnection = getMockWebConnection();
1471             mockWebConnection.setResponse(WebTestCase.URL_FIRST, buildHtml(Mode.SYNC, Execution.ONLY_SEND));
1472             MiniServer.configureDropRequest(new URL(WebTestCase.URL_FIRST + SUCCESS_URL));
1473 
1474             try (MiniServer miniServer = new MiniServer(PORT, mockWebConnection)) {
1475                 miniServer.start();
1476 
1477                 final WebDriver driver = getWebDriver();
1478                 driver.get(WebTestCase.URL_FIRST.toExternalForm());
1479 
1480                 verify(() -> extractLog(driver), String.join("\n", getExpectedAlerts()));
1481 
1482                 // no chance to to check the request count because of retries
1483                 assertEquals(new URL(WebTestCase.URL_FIRST, SUCCESS_URL),
1484                         mockWebConnection.getLastWebRequest().getUrl());
1485                 assertEquals(HttpMethod.GET,
1486                         mockWebConnection.getLastWebRequest().getHttpMethod());
1487             }
1488         }
1489 
1490         /**
1491          * NoHttpResponseException after preflight.
1492          * @throws Exception if the test fails
1493          */
1494         @Test
1495         @Alerts(DEFAULT = {"readystatechange_1_0_true", "open-done: 1_0", "ExceptionThrown"},
1496                 FF = {"readystatechange_1_0_true", "open-done: 1_0", "readystatechange_4_0_true",
1497                       "error_4_0_false", "loadend_4_0_false", "ExceptionThrown"},
1498                 FF_ESR = {"readystatechange_1_0_true", "open-done: 1_0", "readystatechange_4_0_true",
1499                           "error_4_0_false", "loadend_4_0_false", "ExceptionThrown"})
1500         public void onKeyWord_sync_preflight_NoHttpResponseException() throws Exception {
1501             final MockWebConnection mockWebConnection = getMockWebConnection();
1502             mockWebConnection.setResponse(WebTestCase.URL_FIRST, buildHtml(Mode.SYNC, Execution.ONLY_SEND_PREFLIGHT));
1503 
1504             final List<NameValuePair> headers = new ArrayList<>();
1505             headers.add(new NameValuePair("Access-Control-Allow-Origin", "*"));
1506             headers.add(new NameValuePair("Access-Control-Allow-Methods", "*"));
1507             headers.add(new NameValuePair("Access-Control-Allow-Headers", "X-PINGOTHER"));
1508             getMockWebConnection().setResponse(new URL("http://localhost:" + PORT2 + SUCCESS_URL),
1509                     "",  200, "OK", MimeType.TEXT_HTML, headers);
1510             MiniServer.configureDropGetRequest(new URL("http://localhost:" + PORT2 + SUCCESS_URL));
1511 
1512             try (MiniServer miniServer1 = new MiniServer(PORT, mockWebConnection)) {
1513                 miniServer1.start();
1514 
1515                 try (MiniServer miniServer2 = new MiniServer(PORT2, mockWebConnection)) {
1516                     miniServer2.start();
1517 
1518                     final WebDriver driver = getWebDriver();
1519                     driver.get(WebTestCase.URL_FIRST.toExternalForm());
1520 
1521                     verify(() -> extractLog(driver), String.join("\n", getExpectedAlerts()));
1522 
1523                     // no chance to to check the request count because of retries
1524                     assertEquals(new URL("http://localhost:" + PORT2 + SUCCESS_URL),
1525                             mockWebConnection.getLastWebRequest().getUrl());
1526                     assertEquals(HttpMethod.GET,
1527                             mockWebConnection.getLastWebRequest().getHttpMethod());
1528                 }
1529             }
1530         }
1531 
1532         /**
1533          * NoHttpResponseException during preflight.
1534          * @throws Exception if the test fails
1535          */
1536         @Test
1537         @Alerts(DEFAULT = {"readystatechange_1_0_true", "open-done: 1_0", "ExceptionThrown"},
1538                 FF = {"readystatechange_1_0_true", "open-done: 1_0", "readystatechange_4_0_true",
1539                       "error_4_0_false", "loadend_4_0_false", "ExceptionThrown"},
1540                 FF_ESR = {"readystatechange_1_0_true", "open-done: 1_0", "readystatechange_4_0_true",
1541                           "error_4_0_false", "loadend_4_0_false", "ExceptionThrown"})
1542         public void onKeyWord_sync_preflight_NoHttpResponseException_during_preflight() throws Exception {
1543             final MockWebConnection mockWebConnection = getMockWebConnection();
1544             mockWebConnection.setResponse(WebTestCase.URL_FIRST, buildHtml(Mode.SYNC, Execution.ONLY_SEND_PREFLIGHT));
1545             MiniServer.configureDropRequest(new URL("http://localhost:" + PORT2 + SUCCESS_URL));
1546 
1547             try (MiniServer miniServer1 = new MiniServer(PORT, mockWebConnection)) {
1548                 miniServer1.start();
1549 
1550                 try (MiniServer miniServer2 = new MiniServer(PORT2, mockWebConnection)) {
1551                     miniServer2.start();
1552 
1553                     final WebDriver driver = getWebDriver();
1554                     driver.get(WebTestCase.URL_FIRST.toExternalForm());
1555 
1556                     verify(() -> extractLog(driver), String.join("\n", getExpectedAlerts()));
1557 
1558                     // no chance to to check the request count because of retries
1559                     assertEquals(new URL("http://localhost:" + PORT2 + SUCCESS_URL),
1560                             mockWebConnection.getLastWebRequest().getUrl());
1561                     assertEquals(HttpMethod.OPTIONS,
1562                             mockWebConnection.getLastWebRequest().getHttpMethod());
1563                 }
1564             }
1565         }
1566 
1567         /**
1568          * NoHttpResponseException.
1569          * @throws Exception if the test fails
1570          */
1571         @Test
1572         @Alerts({"readystatechange_1_0_true", "open-done: 1_0", "loadstart_1_0_false",
1573                  "send-done: 1_0", "readystatechange_4_0_true", "error_4_0_false",
1574                  "loadend_4_0_false"})
1575         public void onKeyWord_async_NoHttpResponseException() throws Exception {
1576             final MockWebConnection mockWebConnection = getMockWebConnection();
1577             mockWebConnection.setResponse(WebTestCase.URL_FIRST, buildHtml(Mode.ASYNC, Execution.ONLY_SEND));
1578             MiniServer.configureDropRequest(new URL(WebTestCase.URL_FIRST + SUCCESS_URL));
1579 
1580             try (MiniServer miniServer = new MiniServer(PORT, mockWebConnection)) {
1581                 miniServer.start();
1582 
1583                 final WebDriver driver = getWebDriver();
1584                 driver.get(WebTestCase.URL_FIRST.toExternalForm());
1585 
1586                 verify(() -> extractLog(driver), String.join("\n", getExpectedAlerts()));
1587 
1588                 // no chance to to check the request count because of retries
1589                 assertEquals(new URL(WebTestCase.URL_FIRST, SUCCESS_URL),
1590                         mockWebConnection.getLastWebRequest().getUrl());
1591                 assertEquals(HttpMethod.GET,
1592                         mockWebConnection.getLastWebRequest().getHttpMethod());
1593             }
1594         }
1595 
1596         /**
1597          * NoHttpResponseException after preflight.
1598          * @throws Exception if the test fails
1599          */
1600         @Test
1601         @Alerts({"readystatechange_1_0_true", "open-done: 1_0", "loadstart_1_0_false",
1602                  "send-done: 1_0", "readystatechange_4_0_true", "error_4_0_false",
1603                  "loadend_4_0_false"})
1604         public void onKeyWord_async_preflight_NoHttpResponseException() throws Exception {
1605             final MockWebConnection mockWebConnection = getMockWebConnection();
1606             mockWebConnection.setResponse(WebTestCase.URL_FIRST, buildHtml(Mode.ASYNC, Execution.ONLY_SEND_PREFLIGHT));
1607 
1608             final List<NameValuePair> headers = new ArrayList<>();
1609             headers.add(new NameValuePair("Access-Control-Allow-Origin", "*"));
1610             headers.add(new NameValuePair("Access-Control-Allow-Methods", "*"));
1611             headers.add(new NameValuePair("Access-Control-Allow-Headers", "X-PINGOTHER"));
1612             getMockWebConnection().setResponse(new URL("http://localhost:" + PORT2 + SUCCESS_URL),
1613                     "",  200, "OK", MimeType.TEXT_HTML, headers);
1614             MiniServer.configureDropGetRequest(new URL("http://localhost:" + PORT2 + SUCCESS_URL));
1615 
1616             try (MiniServer miniServer1 = new MiniServer(PORT, mockWebConnection)) {
1617                 miniServer1.start();
1618 
1619                 try (MiniServer miniServer2 = new MiniServer(PORT2, mockWebConnection)) {
1620                     miniServer2.start();
1621 
1622                     final WebDriver driver = getWebDriver();
1623                     driver.get(WebTestCase.URL_FIRST.toExternalForm());
1624 
1625                     verify(() -> extractLog(driver), String.join("\n", getExpectedAlerts()));
1626 
1627                     // no chance to to check the request count because of retries
1628                     assertEquals(new URL("http://localhost:" + PORT2 + SUCCESS_URL),
1629                             mockWebConnection.getLastWebRequest().getUrl());
1630                     assertEquals(HttpMethod.GET,
1631                             mockWebConnection.getLastWebRequest().getHttpMethod());
1632                 }
1633             }
1634         }
1635 
1636         /**
1637          * NoHttpResponseException during preflight.
1638          * @throws Exception if the test fails
1639          */
1640         @Test
1641         @Alerts({"readystatechange_1_0_true", "open-done: 1_0", "loadstart_1_0_false",
1642                  "send-done: 1_0", "readystatechange_4_0_true", "error_4_0_false",
1643                  "loadend_4_0_false"})
1644         public void onKeyWord_async_preflight_NoHttpResponseException_during_preflight() throws Exception {
1645             final MockWebConnection mockWebConnection = getMockWebConnection();
1646             mockWebConnection.setResponse(WebTestCase.URL_FIRST, buildHtml(Mode.ASYNC, Execution.ONLY_SEND_PREFLIGHT));
1647             MiniServer.configureDropRequest(new URL("http://localhost:" + PORT2 + SUCCESS_URL));
1648 
1649             try (MiniServer miniServer1 = new MiniServer(PORT, mockWebConnection)) {
1650                 miniServer1.start();
1651 
1652                 try (MiniServer miniServer2 = new MiniServer(PORT2, mockWebConnection)) {
1653                     miniServer2.start();
1654 
1655                     final WebDriver driver = getWebDriver();
1656                     driver.get(WebTestCase.URL_FIRST.toExternalForm());
1657 
1658                     verify(() -> extractLog(driver), String.join("\n", getExpectedAlerts()));
1659 
1660                     // no chance to to check the request count because of retries
1661                     assertEquals(new URL("http://localhost:" + PORT2 + SUCCESS_URL),
1662                             mockWebConnection.getLastWebRequest().getUrl());
1663                     assertEquals(HttpMethod.OPTIONS,
1664                             mockWebConnection.getLastWebRequest().getHttpMethod());
1665                 }
1666             }
1667         }
1668     }
1669 
1670     private static String extractLog(final WebDriver driver) {
1671         return driver.findElement(By.id("log")).getDomProperty("value").trim().replaceAll("\r", "");
1672     }
1673 
1674     /**
1675      * @param mode the {@link Mode}
1676      * @param execution the {@link Execution}
1677      * @return the generated test html
1678      */
1679     private static String buildHtml(final Mode mode, final Execution execution) {
1680         final StringBuffer htmlBuilder = new StringBuffer();
1681         htmlBuilder.append("<html>\n");
1682         htmlBuilder.append("  <head>\n");
1683         htmlBuilder.append("    <title>XMLHttpRequest Test</title>\n");
1684         htmlBuilder.append("    <script>\n");
1685         htmlBuilder.append("      function test() {\n");
1686         htmlBuilder.append("        document.getElementById('log').value = '';\n");
1687         htmlBuilder.append("        xhr = new XMLHttpRequest();\n");
1688         Arrays.asList(State.values()).forEach(state -> registerEventListener(htmlBuilder, mode, state));
1689 
1690         if (Execution.WITHOUT_ORIGIN.equals(execution)
1691                 || Execution.WITHOUT_ORIGIN_PREFLIGHT.equals(execution)) {
1692             htmlBuilder.append("        var url = 'http://' + window.location.hostname + ':"
1693                     + WebTestCase.PORT2 + SUCCESS_WITHOUT_ORIGIN_URL + "';\n");
1694         }
1695         else if (Execution.NETWORK_ERROR.equals(execution)
1696                 || Execution.NETWORK_ERROR_PREFLIGHT.equals(execution)) {
1697             htmlBuilder.append("        var url = 'https://' + window.location.hostname + ':"
1698                                 + WebTestCase.PORT + SUCCESS_URL + "';\n");
1699         }
1700         else if (Execution.ERROR_403.equals(execution)) {
1701             htmlBuilder.append("        var url = '" + ERROR_403_URL + "';\n");
1702         }
1703         else if (Execution.ERROR_403_PREFLIGHT.equals(execution)) {
1704             htmlBuilder.append("        var url = 'http://' + window.location.hostname + ':"
1705                     + WebTestCase.PORT2 + ERROR_403_URL + "';\n");
1706         }
1707         else if (Execution.ERROR_403_DURING_PREFLIGHT.equals(execution)) {
1708             htmlBuilder.append("        var url = 'http://' + window.location.hostname + ':"
1709                     + WebTestCase.PORT2 + PREFLIGHT_ERROR_403_URL + "';\n");
1710         }
1711         else if (Execution.ERROR_500.equals(execution)) {
1712             htmlBuilder.append("        var url = '" + ERROR_500_URL + "';\n");
1713         }
1714         else if (Execution.ERROR_500_PREFLIGHT.equals(execution)) {
1715             htmlBuilder.append("        var url = 'http://' + window.location.hostname + ':"
1716                     + WebTestCase.PORT2 + ERROR_500_URL + "';\n");
1717         }
1718         else if (Execution.ERROR_500_DURING_PREFLIGHT.equals(execution)) {
1719             htmlBuilder.append("        var url = 'http://' + window.location.hostname + ':"
1720                     + WebTestCase.PORT2 + PREFLIGHT_ERROR_500_URL + "';\n");
1721         }
1722         else if (Execution.TIMEOUT.equals(execution)
1723                 || Execution.SEND_ABORT.equals(execution)) {
1724             htmlBuilder.append("        var url = '" + TIMEOUT_URL + "';\n");
1725         }
1726         else if (Execution.ONLY_SEND_PREFLIGHT.equals(execution)
1727                 || Execution.ONLY_SEND_PREFLIGHT_FORBIDDEN.equals(execution)) {
1728             htmlBuilder.append("        var url = 'http://' + window.location.hostname + ':"
1729                     + WebTestCase.PORT2 + SUCCESS_URL + "';\n");
1730         }
1731         else {
1732             htmlBuilder.append("        var url = '" + SUCCESS_URL + "';\n");
1733         }
1734 
1735         htmlBuilder.append("        xhr.open('GET', url, ").append(mode.isAsync()).append(");\n");
1736         htmlBuilder.append("        logText('open-done: ' + xhr.readyState + '_' + xhr.status);\n");
1737 
1738         htmlBuilder.append("        try {\n");
1739 
1740         if (Execution.ONLY_SEND_PREFLIGHT.equals(execution)
1741                 || Execution.WITHOUT_ORIGIN_PREFLIGHT.equals(execution)
1742                 || Execution.NETWORK_ERROR_PREFLIGHT.equals(execution)
1743                 || Execution.ERROR_403_PREFLIGHT.equals(execution)
1744                 || Execution.ERROR_403_DURING_PREFLIGHT.equals(execution)
1745                 || Execution.ERROR_500_PREFLIGHT.equals(execution)
1746                 || Execution.ERROR_500_DURING_PREFLIGHT.equals(execution)) {
1747             htmlBuilder.append("        xhr.setRequestHeader('X-PINGOTHER', 'pingpong');\n");
1748         }
1749         else if (Execution.ONLY_SEND_PREFLIGHT_FORBIDDEN.equals(execution)) {
1750             htmlBuilder.append("        xhr.setRequestHeader('X-FORBIDDEN', 'forbidden');\n");
1751         }
1752 
1753         if (Execution.TIMEOUT.equals(execution)) {
1754             htmlBuilder.append("        xhr.timeout = 10;\n");
1755         }
1756 
1757         htmlBuilder.append("           xhr.send();\n");
1758         htmlBuilder.append("           logText('send-done: ' + xhr.readyState + '_' + xhr.status);\n");
1759         if (Execution.SEND_ABORT.equals(execution)) {
1760             htmlBuilder.append("           xhr.abort();\n");
1761             htmlBuilder.append("           logText('abort-done: ' + xhr.readyState + '_' + xhr.status);\n");
1762         }
1763         htmlBuilder.append("        } catch(e) { logText('ExceptionThrown'); }\n");
1764         htmlBuilder.append("      }\n");
1765 
1766         htmlBuilder.append("      function alertEventState(event) {\n");
1767         htmlBuilder.append("        try {\n");
1768         htmlBuilder.append("          logText(event.type + '_' + xhr.readyState + '_'"
1769                                         + "+ xhr.status + '_' + (event.loaded === undefined));\n");
1770         if (Execution.DONE_ABORT.equals(execution)) {
1771             htmlBuilder.append("          if (xhr.readyState === XMLHttpRequest.DONE) {\n");
1772             htmlBuilder.append("            xhr.abort();\n");
1773             htmlBuilder.append("            logText('abort-done: ' + xhr.readyState + '_' + xhr.status);");
1774             htmlBuilder.append("          }\n");
1775         }
1776         htmlBuilder.append("        } catch(e) { logText('ExceptionThrown abort'); }\n");
1777         htmlBuilder.append("      }\n");
1778 
1779         htmlBuilder.append("      function logText(txt) {\n");
1780         htmlBuilder.append("        document.getElementById('log').value += txt + '\\n';\n");
1781         htmlBuilder.append("      }\n");
1782         htmlBuilder.append("    </script>\n");
1783         htmlBuilder.append("  </head>\n");
1784         htmlBuilder.append("  <body onload='test()'>\n");
1785         htmlBuilder.append("    <textarea id='log' cols='80' rows='40'></textarea>\n");
1786         htmlBuilder.append("  </body>\n");
1787         htmlBuilder.append("</html>");
1788 
1789         return htmlBuilder.toString();
1790     }
1791 
1792     static void registerEventListener(final StringBuffer buffer, final Mode mode, final State state) {
1793         final String function = "alertEventState";
1794 
1795         if (mode.isUseOnKeyword()) {
1796             buffer.append("        xhr.on").append(state.getEventName_()).append("=").append(function).append(";\n");
1797         }
1798         else {
1799             buffer.append("        xhr.addEventListener('").append(state.getEventName_()).append("', ").append(function)
1800                     .append(");\n");
1801         }
1802     }
1803 
1804     private XMLHttpRequestLifeCycleTest() {
1805     }
1806 }