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