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;
16  
17  import org.htmlunit.WebDriverTestCase;
18  import org.htmlunit.junit.annotation.Alerts;
19  import org.htmlunit.junit.annotation.HtmlUnitNYI;
20  import org.junit.jupiter.api.Test;
21  
22  /**
23   * Tests for {@link URLSearchParams}.
24   *
25   * @author Ronald Brill
26   * @author cd alexndr
27   * @author Lai Quang Duong
28   */
29  public class URLSearchParamsTest extends WebDriverTestCase {
30  
31      /**
32       * @throws Exception if an error occurs
33       */
34      @Test
35      @Alerts({"foo=1&bar=2", ""})
36      public void ctor() throws Exception {
37          final String html = DOCTYPE_HTML
38              + "<html>\n"
39              + "<head>\n"
40              + "  <script>\n"
41              + LOG_TITLE_FUNCTION
42              + "    function test() {\n"
43              + "      if (self.URLSearchParams) {\n"
44              + "        log(new URLSearchParams('?foo=1&bar=2'));\n"
45              + "        log(new URLSearchParams());\n"
46              + "      }\n"
47              + "    }\n"
48              + "  </script>\n"
49              + "</head>\n"
50              + "<body onload='test()'>\n"
51              + "</body>\n"
52              + "</html>";
53  
54          loadPageVerifyTitle2(html);
55      }
56  
57      /**
58       * @throws Exception if an error occurs
59       */
60      @Test
61      @Alerts({"",
62               "Failed to construct 'URLSearchParams': The provided value cannot be converted to a sequence",
63               "foo=1&bar=2&null=null",
64               "Failed to construct 'URLSearchParams': Sequence initializer must only contain pair elements",
65               "a%2Cb=1%2C2%2C3",
66               "Failed to construct 'URLSearchParams': The object must have a callable @@iterator property"})
67      public void ctorArray() throws Exception {
68          final String html = DOCTYPE_HTML
69              + "<html>\n"
70              + "<head>\n"
71              + "  <script>\n"
72              + LOG_TITLE_FUNCTION
73              + "    function test() {\n"
74              + "      if (self.URLSearchParams) {\n"
75              + "        log(new URLSearchParams(Array(0)));\n"
76              + "        try {\n"
77              + "          new URLSearchParams(Array(1));\n"
78              + "        } catch (ex) {\n"
79              + "          log(\"Failed to construct 'URLSearchParams': "
80                                  + "The provided value cannot be converted to a sequence\");\n"
81              + "        }\n"
82              + "\n"
83              + "        log(new URLSearchParams([\n"
84              + "          ['foo', '1'],\n"
85              + "          ['bar', '2'],\n"
86              + "          [null, null],\n"
87              + "        ]));\n"
88              + "\n"
89              + "        try {\n"
90              + "          new URLSearchParams([\n"
91              + "            ['foo', '1', '2'],\n"
92              + "          ]);\n"
93              + "        } catch (ex) {\n"
94              + "          log(\"Failed to construct 'URLSearchParams': "
95                                  + "Sequence initializer must only contain pair elements\");\n"
96              + "        }\n"
97              + "\n"
98              + "        log(new URLSearchParams([\n"
99              + "          [\n"
100             + "            ['a', 'b'],\n"
101             + "            ['1', '2', '3']\n"
102             + "          ],\n"
103             + "        ]));\n"
104             + "\n"
105             + "        try {\n"
106             + "          new URLSearchParams([\n"
107             + "            {'foo': 1},\n"
108             + "            {'bar': 2},\n"
109             + "          ]);\n"
110             + "        } catch (ex) {\n"
111             + "          log(\"Failed to construct 'URLSearchParams': "
112                                 + "The object must have a callable @@iterator property\");\n"
113             + "        }\n"
114             + "      }\n"
115             + "    }\n"
116             + "  </script>\n"
117             + "</head>\n"
118             + "<body onload='test()'>\n"
119             + "</body>\n"
120             + "</html>";
121 
122         loadPageVerifyTitle2(html);
123     }
124 
125     /**
126      * @throws Exception if an error occurs
127      */
128     @Test
129     @Alerts({"foo=1&bar=2", "foo=%5Bobject+Object%5D&bar=3"})
130     public void ctorObject() throws Exception {
131         final String html = DOCTYPE_HTML
132             + "<html>\n"
133             + "<head>\n"
134             + "  <script>\n"
135             + LOG_TITLE_FUNCTION
136             + "    function test() {\n"
137             + "      if (self.URLSearchParams) {\n"
138             + "        log(new URLSearchParams({\n"
139             + "          foo: '1',\n"
140             + "          bar: '2'\n"
141             + "        }));\n"
142             + "\n"
143             + "        log(new URLSearchParams({\n"
144             + "          'foo': {\n"
145             + "            'x': '1',\n"
146             + "            'y': '2'\n"
147             + "          },\n"
148             + "          'bar': '3',\n"
149             + "        }))\n;"
150             + "      }\n"
151             + "    }\n"
152             + "  </script>\n"
153             + "</head>\n"
154             + "<body onload='test()'>\n"
155             + "</body>\n"
156             + "</html>";
157 
158         loadPageVerifyTitle2(html);
159     }
160 
161     /**
162      * @throws Exception if an error occurs
163      */
164     @Test
165     @Alerts({"foo=1&bar=%5Bobject+Object%5D&foobar=4%2C5%2C6",
166              "foo=1&bar=2", "foo=1&bar=2",
167              "Failed to construct 'URLSearchParams': Iterator object must be an object",
168              "Failed to construct 'URLSearchParams': @@iterator must be a callable",
169              "Failed to construct 'URLSearchParams': Expected next() function on iterator",
170              "foo=1&bar=2",
171              "Failed to construct 'URLSearchParams': The provided value cannot be converted to a sequence"})
172     public void ctorIterable() throws Exception {
173         final String html = DOCTYPE_HTML
174             + "<html>\n"
175             + "<head>\n"
176             + "  <script>\n"
177             + LOG_TITLE_FUNCTION
178             + "    function test() {\n"
179             + "      if (self.URLSearchParams) {\n"
180             + "        var map = new Map();\n"
181             + "        map.set('foo', 1);\n"
182             + "        map.set('bar', {\n"
183             + "          'x': 1,\n"
184             + "          'y': 2,\n"
185             + "        });\n"
186             + "        map.set('foobar', [4, 5, 6]);\n"
187             + "        log(new URLSearchParams(map));\n"
188             + "\n"
189             + "        var formData = new FormData();\n"
190             + "        formData.append('foo', '1');\n"
191             + "        formData.append('bar', '2');\n"
192             + "        log(new URLSearchParams(formData));\n"
193             + "\n"
194             + "        var searchParams = new URLSearchParams('foo=1&bar=2');\n"
195             + "        log(new URLSearchParams(searchParams));\n"
196             + "\n"
197             + "        try {\n"
198             + "          var brokenIterable = {};\n"
199             + "          brokenIterable[Symbol.iterator] = function (){};\n"
200             + "          new URLSearchParams(brokenIterable);\n"
201             + "        } catch (ex) {\n"
202             + "          log(\"Failed to construct 'URLSearchParams': Iterator object must be an object\");\n"
203             + "        }\n"
204             + "\n"
205             + "        try {\n"
206             + "          var brokenIterable = {};\n"
207             + "          brokenIterable[Symbol.iterator] = 1;\n"
208             + "          new URLSearchParams(brokenIterable);\n"
209             + "        } catch (ex) {\n"
210             + "          log(\"Failed to construct 'URLSearchParams': @@iterator must be a callable\");\n"
211             + "        }\n"
212             + "\n"
213             + "        try {\n"
214             + "          var brokenIterable = {};\n"
215             + "          brokenIterable[Symbol.iterator] = function (){ return {} };\n"
216             + "          new URLSearchParams(brokenIterable);\n"
217             + "        } catch (ex) {\n"
218             + "          log(\"Failed to construct 'URLSearchParams': Expected next() function on iterator\");\n"
219             + "        }\n"
220             + "\n"
221             + "        var generatorObject = (function* () {\n"
222             + "          yield ['foo', 1];\n"
223             + "          yield ['bar', 2];\n"
224             + "        })();\n"
225             + "        log(new URLSearchParams(generatorObject));\n"
226             + "\n"
227             + "        try {\n"
228             + "          var generatorObject = (function* () {\n"
229             + "            yield 1;\n"
230             + "            yield 2;\n"
231             + "          })();\n"
232             + "          new URLSearchParams(generatorObject);\n"
233             + "        } catch (ex) {\n"
234             + "          log(\"Failed to construct 'URLSearchParams': "
235                                 + "The provided value cannot be converted to a sequence\");\n"
236             + "        }\n"
237             + "      }\n"
238             + "    }\n"
239             + "  </script>\n"
240             + "</head>\n"
241             + "<body onload='test()'>\n"
242             + "</body>\n"
243             + "</html>";
244 
245         loadPageVerifyTitle2(html);
246     }
247 
248     /**
249      * @throws Exception if an error occurs
250      */
251     @Test
252     @Alerts({"", "foo=1&bar=2", "q=Html+Unit&unml=%C3%A4%C3%9C", "q=HtmlUnit&u=%3F%3F"})
253     public void string() throws Exception {
254         final String html = DOCTYPE_HTML
255             + "<html>\n"
256             + "<head>\n"
257             + "  <script>\n"
258             + LOG_TITLE_FUNCTION
259             + "    function test() {\n"
260             + "      if (self.URLSearchParams) {\n"
261             + "        log(new URLSearchParams());\n"
262             + "        log(new URLSearchParams('?foo=1&bar=2'));\n"
263 
264             + "        searchParams = new URLSearchParams();\n"
265             + "        searchParams.append('q', 'Html Unit');\n"
266             + "        searchParams.append('unml', '\u00E4\u00DC');\n"
267             + "        log(searchParams);\n"
268 
269             + "        searchParams = new URLSearchParams();\n"
270             + "        searchParams.append('q', 'HtmlUnit');\n"
271             + "        searchParams.append('u', '\u043B\u0189');\n"
272             + "        log(searchParams);\n"
273             + "      }\n"
274             + "    }\n"
275             + "  </script>\n"
276             + "</head>\n"
277             + "<body onload='test()'>\n"
278             + "</body>\n"
279             + "</html>";
280 
281         loadPageVerifyTitle2(html);
282     }
283 
284     /**
285      * @throws Exception if an error occurs
286      */
287     @Test
288     @Alerts({"=emptyKey", "null=nullKey", "undefined=undefinedKey"})
289     public void stringKeys() throws Exception {
290         final String html = DOCTYPE_HTML
291             + "<html>\n"
292             + "<head>\n"
293             + "  <script>\n"
294             + LOG_TITLE_FUNCTION
295             + "    function test() {\n"
296             + "      if (self.URLSearchParams) {\n"
297             + "        searchParams = new URLSearchParams();\n"
298             + "        searchParams.append('', 'emptyKey');\n"
299             + "        log(searchParams);\n"
300 
301             + "        searchParams = new URLSearchParams();\n"
302             + "        searchParams.append(null, 'nullKey');\n"
303             + "        log(searchParams);\n"
304 
305             + "        searchParams = new URLSearchParams();\n"
306             + "        searchParams.append(undefined, 'undefinedKey');\n"
307             + "        log(searchParams);\n"
308             + "      }\n"
309             + "    }\n"
310             + "  </script>\n"
311             + "</head>\n"
312             + "<body onload='test()'>\n"
313             + "</body>\n"
314             + "</html>";
315 
316         loadPageVerifyTitle2(html);
317     }
318 
319     /**
320      * @throws Exception if an error occurs
321      */
322     @Test
323     @Alerts({"emptyValue=", "nullValue=null", "undefinedValue=undefined"})
324     public void stringValues() throws Exception {
325         final String html = DOCTYPE_HTML
326             + "<html>\n"
327             + "<head>\n"
328             + "  <script>\n"
329             + LOG_TITLE_FUNCTION
330             + "    function test() {\n"
331             + "      if (self.URLSearchParams) {\n"
332             + "        searchParams = new URLSearchParams();\n"
333             + "        searchParams.append('emptyValue', '');\n"
334             + "        log(searchParams);\n"
335 
336             + "        searchParams = new URLSearchParams();\n"
337             + "        searchParams.append('nullValue', null);\n"
338             + "        log(searchParams);\n"
339 
340             + "        searchParams = new URLSearchParams();\n"
341             + "        searchParams.append('undefinedValue', undefined);\n"
342             + "        log(searchParams);\n"
343             + "      }\n"
344             + "    }\n"
345             + "  </script>\n"
346             + "</head>\n"
347             + "<body onload='test()'>\n"
348             + "</body>\n"
349             + "</html>";
350 
351         loadPageVerifyTitle2(html);
352     }
353 
354     /**
355      * @throws Exception if an error occurs
356      */
357     @Test
358     @Alerts("TypeError")
359     @HtmlUnitNYI(CHROME = "noValue=undefined",
360             EDGE = "noValue=undefined",
361             FF = "noValue=undefined",
362             FF_ESR = "noValue=undefined")
363     public void stringMissingParam() throws Exception {
364         final String html = DOCTYPE_HTML
365             + "<html>\n"
366             + "<head>\n"
367             + "  <script>\n"
368             + LOG_TITLE_FUNCTION
369             + "    function test() {\n"
370             + "      if (self.URLSearchParams) {\n"
371             + "        try {\n"
372             + "          searchParams = new URLSearchParams();\n"
373             + "          searchParams.append('noValue');\n"
374             + "          log(searchParams);\n"
375             + "        } catch(e) { logEx(e); }\n"
376             + "      }\n"
377             + "    }\n"
378             + "  </script>\n"
379             + "</head>\n"
380             + "<body onload='test()'>\n"
381             + "</body>\n"
382             + "</html>";
383 
384         loadPageVerifyTitle2(html);
385     }
386 
387     /**
388      * @throws Exception if an error occurs
389      */
390     @Test
391     @Alerts({"key=value", "key=value&empty-key=undefined",
392              "key=value&empty-key=undefined&key=overwrite",
393              "key=value&empty-key=undefined&key=overwrite&key-null=null"})
394     public void append() throws Exception {
395         final String html = DOCTYPE_HTML
396             + "<html>\n"
397             + "<head>\n"
398             + "  <script>\n"
399             + LOG_TITLE_FUNCTION
400             + "    function test() {\n"
401             + "      if (self.URLSearchParams) {\n"
402             + "        var param = new URLSearchParams();\n"
403             + "        param.append('key', 'value');\n"
404             + "        log(param);\n"
405             + "        param.append('empty-key', undefined);\n"
406             + "        log(param);\n"
407             + "        param.append('key', 'overwrite');\n"
408             + "        log(param);\n"
409             + "        param.append('key-null', null);\n"
410             + "        log(param);\n"
411             + "      }\n"
412             + "    }\n"
413             + "  </script>\n"
414             + "</head>\n"
415             + "<body onload='test()'>\n"
416             + "</body>\n"
417             + "</html>";
418 
419         loadPageVerifyTitle2(html);
420     }
421 
422     /**
423      * @throws Exception if an error occurs
424      */
425     @Test
426     @Alerts({"%3Fkey%3D%26=value", "true",
427              "%3Fkey%3D%26=value&url=http%3A%2F%2Ffoo.com%2F%3Fx%3D1%26y%3D2%26z%3D3",
428              "http://foo.com/?x=1&y=2&z=3"})
429     public void appendSpecialChars() throws Exception {
430         final String html = DOCTYPE_HTML
431                 + "<html>\n"
432                 + "<head>\n"
433                 + "  <script>\n"
434                 + LOG_TITLE_FUNCTION
435                 + "    function test() {\n"
436                 + "      if (self.URLSearchParams) {\n"
437                 + "        var param = new URLSearchParams();\n"
438                 + "        param.append('?key=&', 'value');\n"
439                 + "        log(param);\n"
440                 + "        log(param.has('?key=&'));\n"
441                 + "        param.append('url', 'http://foo.com/?x=1&y=2&z=3');\n"
442                 + "        log(param);\n"
443                 + "        log(param.get('url'));\n"
444                 + "      }\n"
445                 + "    }\n"
446                 + "  </script>\n"
447                 + "</head>\n"
448                 + "<body onload='test()'>\n"
449                 + "</body>\n"
450                 + "</html>";
451 
452         loadPageVerifyTitle2(html);
453     }
454 
455     /**
456      * @throws Exception if an error occurs
457      */
458     @Test
459     @Alerts({"key=value", "key=value&empty-key=undefined",
460              "key=value&empty-key=undefined&key=overwrite",
461              "key=value&empty-key=undefined&key=overwrite&key-null=null",
462              "http://test.com/p?key=value&empty-key=undefined&key=overwrite&key-null=null"})
463     public void appendFromUrl() throws Exception {
464         final String html = DOCTYPE_HTML
465             + "<html>\n"
466             + "<head>\n"
467             + "  <script>\n"
468             + LOG_TITLE_FUNCTION
469             + "    function test() {\n"
470             + "      if (self.URLSearchParams) {\n"
471             + "        var url = new URL('http://test.com/p');\n"
472             + "        var param = url.searchParams;\n"
473             + "        param.append('key', 'value');\n"
474             + "        log(param);\n"
475             + "        param.append('empty-key', undefined);\n"
476             + "        log(param);\n"
477             + "        param.append('key', 'overwrite');\n"
478             + "        log(param);\n"
479             + "        param.append('key-null', null);\n"
480             + "        log(param);\n"
481 
482             + "        log(url);\n"
483             + "      }\n"
484             + "    }\n"
485             + "  </script>\n"
486             + "</head>\n"
487             + "<body onload='test()'>\n"
488             + "</body>\n"
489             + "</html>";
490 
491         loadPageVerifyTitle2(html);
492     }
493 
494     /**
495      * @throws Exception if an error occurs
496      */
497     @Test
498     @Alerts({"key2=val2&key2=val3", "", "", "", ""})
499     public void delete() throws Exception {
500         final String html = DOCTYPE_HTML
501             + "<html>\n"
502             + "<head>\n"
503             + "  <script>\n"
504             + LOG_TITLE_FUNCTION
505             + "    function test() {\n"
506             + "      if (self.URLSearchParams) {\n"
507             + "        var param = new URLSearchParams('key1=val1&key2=val2&key2=val3');\n"
508             + "        param.delete('key1');\n"
509             + "        log(param);\n"
510             + "        param.delete('key2');\n"
511             + "        log(param);\n"
512             + "        param.delete('key3');\n"
513             + "        log(param);\n"
514             + "        param.delete(undefined);\n"
515             + "        log(param);\n"
516             + "        param.delete(null);\n"
517             + "        log(param);\n"
518             + "      }\n"
519             + "    }\n"
520             + "  </script>\n"
521             + "</head>\n"
522             + "<body onload='test()'>\n"
523             + "</body>\n"
524             + "</html>";
525 
526         loadPageVerifyTitle2(html);
527     }
528 
529     /**
530      * @throws Exception if an error occurs
531      */
532     @Test
533     @Alerts({"key2=val2&key2=val3", "", "", "", "", "http://test.com/p"})
534     public void deleteFromUrl() throws Exception {
535         final String html = DOCTYPE_HTML
536             + "<html>\n"
537             + "<head>\n"
538             + "  <script>\n"
539             + LOG_TITLE_FUNCTION
540             + "    function test() {\n"
541             + "      if (self.URLSearchParams) {\n"
542             + "        var url = new URL('http://test.com/p?key1=val1&key2=val2&key2=val3');\n"
543             + "        var param = url.searchParams;\n"
544             + "        param.delete('key1');\n"
545             + "        log(param);\n"
546             + "        param.delete('key2');\n"
547             + "        log(param);\n"
548             + "        param.delete('key3');\n"
549             + "        log(param);\n"
550             + "        param.delete(undefined);\n"
551             + "        log(param);\n"
552             + "        param.delete(null);\n"
553             + "        log(param);\n"
554 
555             + "        log(url);\n"
556             + "      }\n"
557             + "    }\n"
558             + "  </script>\n"
559             + "</head>\n"
560             + "<body onload='test()'>\n"
561             + "</body>\n"
562             + "</html>";
563 
564         loadPageVerifyTitle2(html);
565     }
566 
567     /**
568      * @throws Exception if an error occurs
569      */
570     @Test
571     @Alerts({"key+1=val1&key2=val2", "http://test.com/p?key%201=val1&key2=val2",
572              "key2=val2", "http://test.com/p?key2=val2"})
573     @HtmlUnitNYI(CHROME = {"key+1=val1&key2=val2", "http://test.com/p?key 1=val1&key2=val2",
574                            "key2=val2", "http://test.com/p?key2=val2"},
575                  EDGE = {"key+1=val1&key2=val2", "http://test.com/p?key 1=val1&key2=val2",
576                          "key2=val2", "http://test.com/p?key2=val2"},
577                  FF = {"key+1=val1&key2=val2", "http://test.com/p?key 1=val1&key2=val2",
578                        "key2=val2", "http://test.com/p?key2=val2"},
579                  FF_ESR = {"key+1=val1&key2=val2", "http://test.com/p?key 1=val1&key2=val2",
580                            "key2=val2", "http://test.com/p?key2=val2"})
581     public void deleteFromUrlSpecialChars() throws Exception {
582         final String html = DOCTYPE_HTML
583             + "<html>\n"
584             + "<head>\n"
585             + "  <script>\n"
586             + LOG_TITLE_FUNCTION
587             + "    function test() {\n"
588             + "      if (self.URLSearchParams) {\n"
589             + "        var url = new URL('http://test.com/p?key 1=val1&key2=val2');\n"
590             + "        var param = url.searchParams;\n"
591             + "        log(param);\n"
592             + "        log(url);\n"
593 
594             + "        param.delete('key 1');\n"
595             + "        log(param);\n"
596             + "        log(url);\n"
597             + "      }\n"
598             + "    }\n"
599             + "  </script>\n"
600             + "</head>\n"
601             + "<body onload='test()'>\n"
602             + "</body>\n"
603             + "</html>";
604 
605         loadPageVerifyTitle2(html);
606     }
607 
608     /**
609      * @throws Exception if an error occurs
610      */
611     @Test
612     @Alerts({"val1", "val2", "null", "null", "null"})
613     public void get() throws Exception {
614         final String html = DOCTYPE_HTML
615             + "<html>\n"
616             + "<head>\n"
617             + "  <script>\n"
618             + LOG_TITLE_FUNCTION
619             + "    function test() {\n"
620             + "      if (self.URLSearchParams) {\n"
621             + "        var param = new URLSearchParams('key1=val1&key2=val2&key1=val3');\n"
622             + "        log(param.get('key1'));\n"
623             + "        log(param.get('key2'));\n"
624             + "        log(param.get('key3'));\n"
625             + "        log(param.get(undefined));\n"
626             + "        log(param.get(null));\n"
627             + "      }\n"
628             + "    }\n"
629             + "  </script>\n"
630             + "</head>\n"
631             + "<body onload='test()'>\n"
632             + "</body>\n"
633             + "</html>";
634 
635         loadPageVerifyTitle2(html);
636     }
637 
638     /**
639      * @throws Exception if an error occurs
640      */
641     @Test
642     @Alerts({"true", "true", "false", "false", "false"})
643     public void has() throws Exception {
644         final String html = DOCTYPE_HTML
645             + "<html>\n"
646             + "<head>\n"
647             + "  <script>\n"
648             + LOG_TITLE_FUNCTION
649             + "    function test() {\n"
650             + "      if (self.URLSearchParams) {\n"
651             + "        var param = new URLSearchParams('key1=val1&key2=val2&key1=val3');\n"
652             + "        log(param.has('key1'));\n"
653             + "        log(param.has('key2'));\n"
654             + "        log(param.has('key3'));\n"
655             + "        log(param.has(undefined));\n"
656             + "        log(param.has(null));\n"
657             + "      }\n"
658             + "    }\n"
659             + "  </script>\n"
660             + "</head>\n"
661             + "<body onload='test()'>\n"
662             + "</body>\n"
663             + "</html>";
664 
665         loadPageVerifyTitle2(html);
666     }
667 
668     /**
669      * @throws Exception if an error occurs
670      */
671     @Test
672     @Alerts({"val1,val3", "val2", "", "", ""})
673     public void getAll() throws Exception {
674         final String html = DOCTYPE_HTML
675             + "<html>\n"
676             + "<head>\n"
677             + "  <script>\n"
678             + LOG_TITLE_FUNCTION
679             + "    function test() {\n"
680             + "      if (self.URLSearchParams) {\n"
681             + "        var param = new URLSearchParams('key1=val1&key2=val2&key1=val3');\n"
682             + "        log(param.getAll('key1'));\n"
683             + "        log(param.getAll('key2'));\n"
684             + "        log(param.getAll('key3'));\n"
685             + "        log(param.getAll(undefined));\n"
686             + "        log(param.getAll(null));\n"
687             + "      }\n"
688             + "    }\n"
689             + "  </script>\n"
690             + "</head>\n"
691             + "<body onload='test()'>\n"
692             + "</body>\n"
693             + "</html>";
694 
695         loadPageVerifyTitle2(html);
696     }
697 
698     /**
699      * @throws Exception if an error occurs
700      */
701     @Test
702     @Alerts({"key1=val1&key2=val2&key2=val3&key4=val4",
703              "key1=new1&key2=val2&key2=val3&key4=val4",
704              "key1=new1&key2=new2&key4=val4",
705              "key1=new1&key2=new2&key4=val4&key3=undefined",
706              "key1=new1&key2=new2&key4=null&key3=undefined"})
707     public void set() throws Exception {
708         final String html = DOCTYPE_HTML
709             + "<html>\n"
710             + "<head>\n"
711             + "  <script>\n"
712             + LOG_TITLE_FUNCTION
713             + "    function test() {\n"
714             + "      if (self.URLSearchParams) {\n"
715             + "        var param = new URLSearchParams('key1=val1&key2=val2&key2=val3');\n"
716             + "        param.set('key4', 'val4');\n"
717             + "        log(param);\n"
718             + "        param.set('key1', 'new1');\n"
719             + "        log(param);\n"
720             + "        param.set('key2', 'new2');\n"
721             + "        log(param);\n"
722             + "        param.set('key3', undefined);\n"
723             + "        log(param);\n"
724             + "        param.set('key4', null);\n"
725             + "        log(param);\n"
726             + "      }\n"
727             + "    }\n"
728             + "  </script>\n"
729             + "</head>\n"
730             + "<body onload='test()'>\n"
731             + "</body>\n"
732             + "</html>";
733 
734         loadPageVerifyTitle2(html);
735     }
736 
737 
738     /**
739      * @throws Exception if an error occurs
740      */
741     @Test
742     @Alerts({"key1=val1&key2=val2&key2=val3&key4=val4",
743              "key1=new1&key2=val2&key2=val3&key4=val4",
744              "key1=new1&key2=new2&key4=val4",
745              "key1=new1&key2=new2&key4=val4&key3=undefined",
746              "key1=new1&key2=new2&key4=null&key3=undefined",
747              "http://test.com/p?key1=new1&key2=new2&key4=null&key3=undefined"})
748     public void setFromUrl() throws Exception {
749         final String html = DOCTYPE_HTML
750             + "<html>\n"
751             + "<head>\n"
752             + "  <script>\n"
753             + LOG_TITLE_FUNCTION
754             + "    function test() {\n"
755             + "      if (self.URLSearchParams) {\n"
756             + "        var url = new URL('http://test.com/p?key1=val1&key2=val2&key2=val3');\n"
757             + "        var param = url.searchParams;\n"
758             + "        param.set('key4', 'val4');\n"
759             + "        log(param);\n"
760             + "        param.set('key1', 'new1');\n"
761             + "        log(param);\n"
762             + "        param.set('key2', 'new2');\n"
763             + "        log(param);\n"
764             + "        param.set('key3', undefined);\n"
765             + "        log(param);\n"
766             + "        param.set('key4', null);\n"
767             + "        log(param);\n"
768 
769             + "        log(url);\n"
770             + "      }\n"
771             + "    }\n"
772             + "  </script>\n"
773             + "</head>\n"
774             + "<body onload='test()'>\n"
775             + "</body>\n"
776             + "</html>";
777 
778         loadPageVerifyTitle2(html);
779     }
780 
781     /**
782      * @throws Exception if an error occurs
783      */
784     @Test
785     @Alerts({"function keys() { [native code] }", "[object URLSearchParams Iterator]",
786              "key1", "key2", "key1", "", "true"})
787     public void keys() throws Exception {
788         final String html = DOCTYPE_HTML
789             + "<html>\n"
790             + "<head>\n"
791             + "  <script>\n"
792             + LOG_TITLE_FUNCTION
793             + "    function test() {\n"
794             + "      if (self.URLSearchParams) {\n"
795             + "        var param = new URLSearchParams('key1=val1&key2=&key1=val3&=val4');\n"
796 
797             + "        log(param.keys);\n"
798             + "        var iter = param.keys();\n"
799             + "        log(iter);\n"
800 
801             + "        var entry = iter.next().value;\n"
802             + "        log(entry);\n"
803             + "        entry = iter.next().value;\n"
804             + "        log(entry);\n"
805             + "        entry = iter.next().value;\n"
806             + "        log(entry);\n"
807             + "        entry = iter.next().value;\n"
808             + "        log(entry);\n"
809 
810             + "        log(iter.next().done);\n"
811             + "      }\n"
812             + "    }\n"
813             + "  </script>\n"
814             + "</head>\n"
815             + "<body onload='test()'>\n"
816             + "</body>\n"
817             + "</html>";
818 
819         loadPageVerifyTitle2(html);
820     }
821 
822     /**
823      * @throws Exception if an error occurs
824      */
825     @Test
826     @Alerts({"function values() { [native code] }", "[object URLSearchParams Iterator]",
827              "val1", "", "val3", "val4", "true"})
828     public void values() throws Exception {
829         final String html = DOCTYPE_HTML
830             + "<html>\n"
831             + "<head>\n"
832             + "  <script>\n"
833             + LOG_TITLE_FUNCTION
834             + "    function test() {\n"
835             + "      if (self.URLSearchParams) {\n"
836             + "        var param = new URLSearchParams('key1=val1&key2=&key1=val3&=val4');\n"
837 
838             + "        log(param.values);\n"
839             + "        var iter = param.values();\n"
840             + "        log(iter);\n"
841 
842             + "        var entry = iter.next().value;\n"
843             + "        log(entry);\n"
844             + "        entry = iter.next().value;\n"
845             + "        log(entry);\n"
846             + "        entry = iter.next().value;\n"
847             + "        log(entry);\n"
848             + "        entry = iter.next().value;\n"
849             + "        log(entry);\n"
850 
851             + "        log(iter.next().done);\n"
852             + "      }\n"
853             + "    }\n"
854             + "  </script>\n"
855             + "</head>\n"
856             + "<body onload='test()'>\n"
857             + "</body>\n"
858             + "</html>";
859 
860         loadPageVerifyTitle2(html);
861     }
862 
863     /**
864      * @throws Exception if an error occurs
865      */
866     @Test
867     @Alerts({"val1", "", "val3", "val4"})
868     public void valuesForOf() throws Exception {
869         final String html = DOCTYPE_HTML
870             + "<html>\n"
871             + "<head>\n"
872             + "  <script>\n"
873             + LOG_TITLE_FUNCTION
874             + "    function test() {\n"
875             + "      if (self.URLSearchParams) {\n"
876             + "        var param = new URLSearchParams('key1=val1&key2=&key1=val3&=val4');\n"
877 
878             + "        for (var i of param.values()) {\n"
879             + "          log(i);\n"
880             + "        }\n"
881             + "      }\n"
882             + "    }\n"
883             + "  </script>\n"
884             + "</head>\n"
885             + "<body onload='test()'>\n"
886             + "</body>\n"
887             + "</html>";
888 
889         loadPageVerifyTitle2(html);
890     }
891 
892     /**
893      * @throws Exception if an error occurs
894      */
895     @Test
896     @Alerts({"key1-val1", "key2-val2", "key3-val3",
897              "key1-val1", "key3-val3",
898              "key2-val2", "key3-val3"})
899     public void forEach() throws Exception {
900         final String html = DOCTYPE_HTML
901             + "<html>\n"
902                 + "<head>\n"
903                 + "  <script>\n"
904                 + LOG_TITLE_FUNCTION
905                 + "    function test() {\n"
906                 + "      if (self.URLSearchParams) {\n"
907                 + "        var param = new URLSearchParams('key1=val1&key2=val2&key3=val3');\n"
908                 + "        param.forEach((value, key) => {\n"
909                 + "          log(key + '-' + value);\n"
910                 + "        });\n"
911                 + "        param.forEach((value, key) => {\n"
912                 + "          log(key + '-' + value);\n"
913                 + "          if (value == 'val1' || value == 'val2') {\n"
914                 + "            param.delete(key);\n"
915                 + "          }\n"
916                 + "        });\n"
917                 + "        param.forEach((value, key) => {\n"
918                 + "          log(key + '-' + value);\n"
919                 + "        });\n"
920                 + "      }\n"
921                 + "    }\n"
922                 + "  </script>\n"
923                 + "</head>\n"
924                 + "<body onload='test()'>\n"
925                 + "</body>\n"
926                 + "</html>";
927 
928         loadPageVerifyTitle2(html);
929     }
930 
931     /**
932      * @throws Exception if the test fails
933      */
934     @Test
935     @Alerts({"TypeError", "TypeError"})
936     public void forEachWrongParam() throws Exception {
937         final String html = DOCTYPE_HTML
938                 + "<html><head><script>\n"
939                 + LOG_TITLE_FUNCTION
940                 + "function test() {\n"
941                 + "  if (self.URLSearchParams) {\n"
942                 + "    var param = new URLSearchParams('key1=val1&key2=val2&key3=val3');\n"
943                 + "    try {\n"
944                 + "      param.forEach();\n"
945                 + "    } catch(e) { logEx(e); }\n"
946                 + "    try {\n"
947                 + "      param.forEach('wrong');\n"
948                 + "    } catch(e) { logEx(e); }\n"
949                 + "  }\n"
950                 + "}\n"
951                 + "</script></head><body onload='test()'>\n"
952                 + "  <div id='d1' class=' a b \t c \n d \u000B e \u000C f \r g'></div>\n"
953                 + "</body></html>";
954 
955         loadPageVerifyTitle2(html);
956     }
957 
958     /**
959      * @throws Exception if an error occurs
960      */
961     @Test
962     @Alerts({"true", "function entries() { [native code] }", "[object URLSearchParams Iterator]",
963              "key1-val1", "key2-", "key1-val3", "-val4", "true"})
964     public void entries() throws Exception {
965         final String html = DOCTYPE_HTML
966             + "<html>\n"
967             + "<head>\n"
968             + "  <script>\n"
969             + LOG_TITLE_FUNCTION
970             + "    function test() {\n"
971             + "      if (self.URLSearchParams) {\n"
972             + "        var param = new URLSearchParams('key1=val1&key2=&key1=val3&=val4');\n"
973 
974             + "        if (typeof Symbol != 'undefined') {\n"
975             + "          log(param[Symbol.iterator] === param.entries);\n"
976             + "        }\n"
977 
978             + "        log(param.entries);\n"
979             + "        var iter = param.entries();\n"
980             + "        log(iter);\n"
981 
982             + "        var entry = iter.next().value;\n"
983             + "        log(entry[0] + '-' + entry[1]);\n"
984             + "        entry = iter.next().value;\n"
985             + "        log(entry[0] + '-' + entry[1]);\n"
986             + "        entry = iter.next().value;\n"
987             + "        log(entry[0] + '-' + entry[1]);\n"
988             + "        entry = iter.next().value;\n"
989             + "        log(entry[0] + '-' + entry[1]);\n"
990 
991             + "        log(iter.next().done);\n"
992             + "      }\n"
993             + "    }\n"
994             + "  </script>\n"
995             + "</head>\n"
996             + "<body onload='test()'>\n"
997             + "</body>\n"
998             + "</html>";
999 
1000         loadPageVerifyTitle2(html);
1001     }
1002 
1003     /**
1004      * @throws Exception if an error occurs
1005      */
1006     @Test
1007     @Alerts({"key1,val1", "key2,", "key1,val3", ",val4"})
1008     public void entriesForOf() throws Exception {
1009         final String html = DOCTYPE_HTML
1010             + "<html>\n"
1011             + "<head>\n"
1012             + "  <script>\n"
1013             + LOG_TITLE_FUNCTION
1014             + "    function test() {\n"
1015             + "      if (self.URLSearchParams) {\n"
1016             + "        var param = new URLSearchParams('key1=val1&key2=&key1=val3&=val4');\n"
1017 
1018             + "        for (var i of param.entries()) {\n"
1019             + "          log(i);\n"
1020             + "        }\n"
1021             + "      }\n"
1022             + "    }\n"
1023             + "  </script>\n"
1024             + "</head>\n"
1025             + "<body onload='test()'>\n"
1026             + "</body>\n"
1027             + "</html>";
1028 
1029         loadPageVerifyTitle2(html);
1030     }
1031 
1032     /**
1033      * @throws Exception if an error occurs
1034      */
1035     @Test
1036     @Alerts({"4", "0", "1"})
1037     public void size() throws Exception {
1038         final String html = DOCTYPE_HTML
1039             + "<html>\n"
1040             + "<head>\n"
1041             + "  <script>\n"
1042             + LOG_TITLE_FUNCTION
1043             + "    function test() {\n"
1044             + "      if (self.URLSearchParams) {\n"
1045             + "        var param = new URLSearchParams('key1=val1&key2=&key1=val3&=val4');\n"
1046             + "        log(param.size);\n"
1047 
1048             + "        param = new URLSearchParams('');\n"
1049             + "        log(param.size);\n"
1050 
1051             + "        param = new URLSearchParams('key1=val1');\n"
1052             + "        log(param.size);\n"
1053             + "      }\n"
1054             + "    }\n"
1055             + "  </script>\n"
1056             + "</head>\n"
1057             + "<body onload='test()'>\n"
1058             + "</body>\n"
1059             + "</html>";
1060 
1061         loadPageVerifyTitle2(html);
1062     }
1063 
1064     /**
1065      * @throws Exception if the test fails
1066      */
1067     @Test
1068     @Alerts({"function URLSearchParams() { [native code] }",
1069              "function URLSearchParams() { [native code] }",
1070              "function URLSearchParams() { [native code] }",
1071              "key1=val1", "key1=val1", "key1=val1"})
1072     public void testToString() throws Exception {
1073         final String html = DOCTYPE_HTML
1074             + "<html><body>\n"
1075             + "<script>\n"
1076             + LOG_TITLE_FUNCTION
1077             + "  if (self.URLSearchParams) {\n"
1078             + "    log(URLSearchParams);\n"
1079             + "    log('' + URLSearchParams);\n"
1080             + "    log(URLSearchParams.toString());\n"
1081 
1082             + "    var p = new URLSearchParams('key1=val1');\n"
1083             + "    log(p);\n"
1084             + "    log('' + p);\n"
1085             + "    log(p.toString());\n"
1086             + "  }"
1087             + "</script>\n"
1088             + "</body></html>";
1089 
1090         loadPageVerifyTitle2(html);
1091     }
1092 }