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.html;
16  
17  import org.htmlunit.WebDriverTestCase;
18  import org.htmlunit.junit.annotation.Alerts;
19  import org.junit.jupiter.api.Test;
20  
21  /**
22   * Tests for {@link HTMLAllCollection}.
23   *
24   * @author Ronald Brill
25   * @author Ahmed Ashour
26   */
27  public class HTMLAllCollectionTest extends WebDriverTestCase {
28  
29      /**
30       * @throws Exception if the test fails
31       */
32      @Test
33      @Alerts("null")
34      public void namedItem_Unknown() throws Exception {
35          namedItem("'foo'");
36      }
37  
38      /**
39       * @throws Exception if the test fails
40       */
41      @Test
42      @Alerts("button1-")
43      public void namedItem_ById() throws Exception {
44          namedItem("'button1'");
45      }
46  
47      /**
48       * @throws Exception if the test fails
49       */
50      @Test
51      @Alerts("-button2")
52      public void namedItem_ByName_formWithoutId() throws Exception {
53          namedItem("'button2'");
54      }
55  
56      /**
57       * @throws Exception if the test fails
58       */
59      @Test
60      @Alerts("b3-button3")
61      public void namedItem_ByName() throws Exception {
62          namedItem("'button3'");
63      }
64  
65      /**
66       * @throws Exception if the test fails
67       */
68      @Test
69      @Alerts({"coll 2", "b4-button4_1", "b4-button4_2"})
70      public void namedItem_DuplicateId() throws Exception {
71          namedItem("'b4'");
72      }
73  
74      /**
75       * @throws Exception if the test fails
76       */
77      @Test
78      @Alerts({"coll 2", "b5_1-button5", "b5_2-button5"})
79      public void namedItem_DuplicateName() throws Exception {
80          namedItem("'button5'");
81      }
82  
83      /**
84       * @throws Exception if the test fails
85       */
86      @Test
87      @Alerts({"coll 2", "b6-button6", "button6-button6_2"})
88      public void namedItem_DuplicateIdName() throws Exception {
89          namedItem("'button6'");
90      }
91  
92      /**
93       * @throws Exception if the test fails
94       */
95      @Test
96      @Alerts("null")
97      public void namedItem_ZeroIndex() throws Exception {
98          namedItem("0");
99      }
100 
101     /**
102      * @throws Exception if the test fails
103      */
104     @Test
105     @Alerts("null")
106     public void namedItem_ValidIndex() throws Exception {
107         namedItem("1");
108     }
109 
110     /**
111      * @throws Exception if the test fails
112      */
113     @Test
114     @Alerts("null")
115     public void namedItem_DoubleIndex() throws Exception {
116         namedItem("1.1");
117     }
118 
119     /**
120      * @throws Exception if the test fails
121      */
122     @Test
123     @Alerts("null")
124     public void namedItem_InvalidIndex() throws Exception {
125         namedItem("200");
126     }
127 
128     /**
129      * @throws Exception if the test fails
130      */
131     @Test
132     @Alerts("null")
133     public void namedItem_IndexAsString() throws Exception {
134         namedItem("'1'");
135     }
136 
137     /**
138      * @throws Exception if the test fails
139      */
140     @Test
141     @Alerts("null")
142     public void namedItem_IndexDoubleAsString() throws Exception {
143         namedItem("'1.1'");
144     }
145 
146     private void namedItem(final String name) throws Exception {
147         final String html = DOCTYPE_HTML
148             + "<html id='myHtml'><head id='myHead'><title id='myTitle'>First</title><script>\n"
149             + "  alerts = ''\n"
150             + "  function log(msg) { alerts += msg + '§';}\n"
151             + "  function report(result) {\n"
152             + "    if (result == null || result == undefined) {\n"
153             + "      log(result);\n"
154             + "    } else if (('length' in result) && ('item' in result)) {\n"
155             + "      log('coll ' + result.length);\n"
156             + "      for(var i = 0; i < result.length; i++) {\n"
157             + "        log(result.item(i).id + '-' + result.item(i).name);\n"
158             + "      }\n"
159             + "    } else if (result.id || result.name) {\n"
160             + "      log(result.id + '-' + result.name);\n"
161             + "    } else {\n"
162             + "      log(result);\n"
163             + "    }\n"
164             + "  }\n"
165 
166             + "  function doTest() {\n"
167             + "    try {\n"
168             + "      var item = document.all.namedItem(" + name + ");\n"
169             + "      report(item);\n"
170             + "    } catch(e) { log(e); }\n"
171             + "    document.title = alerts;"
172             + "  }\n"
173             + "</script></head>\n"
174             + "<body onload='doTest()'>\n"
175             + "  <button id='button1'></button>\n"
176             + "  <button name='button2'></button>\n"
177             + "  <button id='b3' name='button3'></button>\n"
178             + "  <button id='b4' name='button4_1'></button>\n"
179             + "  <button id='b4' name='button4_2'></button>\n"
180             + "  <button id='b5_1' name='button5'></button>\n"
181             + "  <button id='b5_2' name='button5'></button>\n"
182             + "  <button id='b6' name='button6'></button>\n"
183             + "  <button id='button6' name='button6_2'></button>\n"
184             + "</body></html>";
185 
186         loadPageVerifyTitle2(html);
187     }
188 
189     /**
190      * @throws Exception if the test fails
191      */
192     @Test
193     @Alerts("null")
194     public void item_Unknown() throws Exception {
195         item("'foo'");
196     }
197 
198     /**
199      * @throws Exception if the test fails
200      */
201     @Test
202     @Alerts("b2-button2")
203     public void item_ById() throws Exception {
204         item("'b2'");
205     }
206 
207     /**
208      * @throws Exception if the test fails
209      */
210     @Test
211     @Alerts("b2-button2")
212     public void item_ByName() throws Exception {
213         item("'button2'");
214     }
215 
216     /**
217      * @throws Exception if the test fails
218      */
219     @Test
220     @Alerts("null")
221     public void item_NegativIndex() throws Exception {
222         item("-1");
223     }
224 
225     /**
226      * @throws Exception if the test fails
227      */
228     @Test
229     @Alerts("myHtml-undefined")
230     public void item_ZeroIndex() throws Exception {
231         item("0");
232     }
233 
234     /**
235      * @throws Exception if the test fails
236      */
237     @Test
238     @Alerts("myHead-undefined")
239     public void item_ValidIndex() throws Exception {
240         item("1");
241     }
242 
243     /**
244      * @throws Exception if the test fails
245      */
246     @Test
247     @Alerts("null")
248     public void item_DoubleIndex() throws Exception {
249         item("1.1");
250     }
251 
252     /**
253      * @throws Exception if the test fails
254      */
255     @Test
256     @Alerts("null")
257     public void item_InvalidIndex() throws Exception {
258         item("200");
259     }
260 
261     /**
262      * @throws Exception if the test fails
263      */
264     @Test
265     @Alerts("myHead-undefined")
266     public void item_IndexAsString() throws Exception {
267         item("'1'");
268     }
269 
270     /**
271      * @throws Exception if the test fails
272      */
273     @Test
274     @Alerts("null")
275     public void item_IndexDoubleAsString() throws Exception {
276         item("'1.1'");
277     }
278 
279     private void item(final String name) throws Exception {
280         final String html = DOCTYPE_HTML
281             + "<html id='myHtml'>\n"
282             + "<head id='myHead'>\n"
283             + "<script>\n"
284             + LOG_TITLE_FUNCTION
285             + "  function report(result) {\n"
286             + "    if (result == null || result == undefined) {\n"
287             + "      log(result);\n"
288             + "    } else if (('length' in result) && ('item' in result)) {\n"
289             + "      log('coll ' + result.length);\n"
290             + "      for(var i = 0; i < result.length; i++) {\n"
291             + "        log(result.item(i).id + '-' + result.item(i).name);\n"
292             + "      }\n"
293             + "    } else if (result.id || result.name) {\n"
294             + "      log(result.id + '-' + result.name);\n"
295             + "    } else {\n"
296             + "      log(result);\n"
297             + "    }\n"
298             + "  }\n"
299 
300             + "  function doTest() {\n"
301             + "    try {\n"
302             + "      var item = document.all.item(" + name + ");\n"
303             + "      report(item);\n"
304             + "    } catch(e) { logEx(e); }\n"
305             + "  }\n"
306             + "</script></head>\n"
307             + "<body onload='doTest()'>\n"
308             + "  <button id='b1' name='button1'></button>\n"
309             + "  <button id='b2' name='button2'></button>\n"
310             + "</body></html>";
311 
312         loadPageVerifyTitle2(html);
313     }
314 
315     /**
316      * @throws Exception if the test fails
317      */
318     @Test
319     @Alerts("undefined")
320     public void arrayIndex_Unknown() throws Exception {
321         arrayIndex("'foo'");
322     }
323 
324     /**
325      * @throws Exception if the test fails
326      */
327     @Test
328     @Alerts("b2-button2")
329     public void arrayIndex_ById() throws Exception {
330         arrayIndex("'b2'");
331     }
332 
333     /**
334      * @throws Exception if the test fails
335      */
336     @Test
337     @Alerts("b2-button2")
338     public void arrayIndex_ByName() throws Exception {
339         arrayIndex("'button2'");
340     }
341 
342     /**
343      * @throws Exception if the test fails
344      */
345     @Test
346     @Alerts("undefined")
347     public void arrayIndex_NegativIndex() throws Exception {
348         arrayIndex("-1");
349     }
350 
351     /**
352      * @throws Exception if the test fails
353      */
354     @Test
355     @Alerts("myHtml-undefined")
356     public void arrayIndex_ZeroIndex() throws Exception {
357         arrayIndex("0");
358     }
359 
360     /**
361      * @throws Exception if the test fails
362      */
363     @Test
364     @Alerts("myHead-undefined")
365     public void arrayIndex_ValidIndex() throws Exception {
366         arrayIndex("1");
367     }
368 
369     /**
370      * @throws Exception if the test fails
371      */
372     @Test
373     @Alerts("undefined")
374     public void arrayIndex_DoubleIndex() throws Exception {
375         arrayIndex("1.1");
376     }
377 
378     /**
379      * @throws Exception if the test fails
380      */
381     @Test
382     @Alerts("undefined")
383     public void arrayIndex_InvalidIndex() throws Exception {
384         arrayIndex("200");
385     }
386 
387     /**
388      * @throws Exception if the test fails
389      */
390     @Test
391     @Alerts("myScript-undefined")
392     public void arrayIndex_IndexAsString() throws Exception {
393         arrayIndex("'2'");
394     }
395 
396     private void arrayIndex(final String name) throws Exception {
397         final String html = DOCTYPE_HTML
398             + "<html id='myHtml'>\n"
399             + "<head id='myHead'>\n"
400             + "<script id='myScript'>\n"
401             + LOG_TITLE_FUNCTION
402             + "  function report(result) {\n"
403             + "    if (result == null || result == undefined) {\n"
404             + "      log(result);\n"
405             + "    } else if (('length' in result) && ('item' in result)) {\n"
406             + "      log('coll ' + result.length);\n"
407             + "      for(var i = 0; i < result.length; i++) {\n"
408             + "        log(result.item(i).id + '-' + result.item(i).name);\n"
409             + "      }\n"
410             + "    } else if (result.id || result.name) {\n"
411             + "      log(result.id + '-' + result.name);\n"
412             + "    } else {\n"
413             + "      log(result);\n"
414             + "    }\n"
415             + "  }\n"
416 
417             + "  function doTest() {\n"
418             + "    try {\n"
419             + "      var item = document.all[" + name + "];\n"
420             + "      report(item);\n"
421             + "    } catch(e) { logEx(e); }\n"
422             + "  }\n"
423             + "</script></head>\n"
424             + "<body onload='doTest()'>\n"
425             + "  <button id='b1' name='button1'></button>\n"
426             + "  <button id='b2' name='button2'></button>\n"
427             + "</body></html>";
428 
429         loadPageVerifyTitle2(html);
430     }
431 
432     /**
433      * @throws Exception if the test fails
434      */
435     @Test
436     @Alerts("null")
437     public void functionIndex_Unknown() throws Exception {
438         functionIndex("'foo'");
439     }
440 
441     /**
442      * @throws Exception if the test fails
443      */
444     @Test
445     @Alerts("b2-button2")
446     public void functionIndex_ById() throws Exception {
447         functionIndex("'b2'");
448     }
449 
450     /**
451      * @throws Exception if the test fails
452      */
453     @Test
454     @Alerts("b2-button2")
455     public void functionIndex_ByName() throws Exception {
456         functionIndex("'button2'");
457     }
458 
459     /**
460      * @throws Exception if the test fails
461      */
462     @Test
463     @Alerts("null")
464     public void functionIndex_NegativIndex() throws Exception {
465         functionIndex("-1");
466     }
467 
468     /**
469      * @throws Exception if the test fails
470      */
471     @Test
472     @Alerts("myHtml-undefined")
473     public void functionIndex_ZeroIndex() throws Exception {
474         functionIndex("0");
475     }
476 
477     /**
478      * @throws Exception if the test fails
479      */
480     @Test
481     @Alerts("myHead-undefined")
482     public void functionIndex_ValidIndex() throws Exception {
483         functionIndex("1");
484     }
485 
486     /**
487      * @throws Exception if the test fails
488      */
489     @Test
490     @Alerts("null")
491     public void functionIndex_DoubleIndex() throws Exception {
492         functionIndex("1.1");
493     }
494 
495     /**
496      * @throws Exception if the test fails
497      */
498     @Test
499     @Alerts("null")
500     public void functionIndex_InvalidIndex() throws Exception {
501         functionIndex("200");
502     }
503 
504     /**
505      * @throws Exception if the test fails
506      */
507     @Test
508     @Alerts("myScript-undefined")
509     public void functionIndex_IndexAsString() throws Exception {
510         functionIndex("'2'");
511     }
512 
513     private void functionIndex(final String name) throws Exception {
514         final String html = DOCTYPE_HTML
515             + "<html id='myHtml'>\n"
516             + "<head id='myHead'>\n"
517             + "<script id='myScript'>\n"
518             + LOG_TITLE_FUNCTION
519             + "  function report(result) {\n"
520             + "    if (result == null || result == undefined) {\n"
521             + "      log(result);\n"
522             + "    } else if (('length' in result) && ('item' in result)) {\n"
523             + "      log('coll ' + result.length);\n"
524             + "      for(var i = 0; i < result.length; i++) {\n"
525             + "        log(result.item(i).id + '-' + result.item(i).name);\n"
526             + "      }\n"
527             + "    } else if (result.id || result.name) {\n"
528             + "      log(result.id + '-' + result.name);\n"
529             + "    } else {\n"
530             + "      log(result);\n"
531             + "    }\n"
532             + "  }\n"
533 
534             + "  function doTest() {\n"
535             + "    try {\n"
536             + "      var item = document.all(" + name + ");\n"
537             + "      report(item);\n"
538             + "    } catch(e) { logEx(e); }\n"
539             + "  }\n"
540             + "</script></head>\n"
541             + "<body onload='doTest()'>\n"
542             + "  <button id='b1' name='button1'></button>\n"
543             + "  <button id='b2' name='button2'></button>\n"
544             + "</body></html>";
545 
546         loadPageVerifyTitle2(html);
547     }
548 
549     /**
550      * @throws Exception if the test fails
551      */
552     @Test
553     @Alerts({"[object HTMLAllCollection]", "function HTMLAllCollection() { [native code] }"})
554     public void type() throws Exception {
555         final String html = DOCTYPE_HTML
556             + "<html><head>\n"
557             + "<script>\n"
558             + LOG_TITLE_FUNCTION
559             + "  function test() {\n"
560             + "    try {\n"
561             + "      log(document.all);\n"
562             + "      log(HTMLAllCollection);\n"
563             + "    } catch(e) { logEx(e); }\n"
564             + "  }\n"
565             + "</script>\n"
566             + "</head>\n"
567             + "<body onload='test()'>\n"
568             + "</body></html>";
569 
570         loadPageVerifyTitle2(html);
571     }
572 
573     /**
574      * @throws Exception if the test fails
575      */
576     @Test
577     @Alerts("function () { [native code] }")
578     public void proto() throws Exception {
579         final String html = DOCTYPE_HTML
580             + "<html><head>\n"
581             + "<script>\n"
582             + LOG_TITLE_FUNCTION
583             + "  function test() {\n"
584             + "    log(HTMLAllCollection.__proto__);\n"
585             + "  }\n"
586             + "</script>\n"
587             + "</head>\n"
588             + "<body onload='test()'>\n"
589             + "</body></html>";
590 
591         loadPageVerifyTitle2(html);
592     }
593 
594     /**
595      * See https://developer.mozilla.org/en-US/docs/Web/API/HTMLAllCollection#special_type_conversion_behavior.
596      *
597      * @throws Exception if the test fails
598      */
599     @Test
600     @Alerts({"false", "true", "false", "true"})
601     public void looselyEqualToUndefined() throws Exception {
602         final String html = DOCTYPE_HTML
603             + "<html>\n"
604             + "<body>\n"
605             + "<script>\n"
606             + LOG_TITLE_FUNCTION
607             + "  log(undefined === document.all);\n"
608             + "  log(undefined == document.all);\n"
609             + "  log(document.all === undefined);\n"
610             + "  log(document.all == undefined);\n"
611             + "</script>\n"
612             + "</body></html>";
613 
614         loadPageVerifyTitle2(html);
615     }
616 
617     /**
618      * See https://developer.mozilla.org/en-US/docs/Web/API/HTMLAllCollection#special_type_conversion_behavior.
619      *
620      * @throws Exception if the test fails
621      */
622     @Test
623     @Alerts({"false", "true", "false", "true"})
624     public void looselyEqualToNull() throws Exception {
625         final String html = DOCTYPE_HTML
626             + "<html>\n"
627             + "<body>\n"
628             + "<script>\n"
629             + LOG_TITLE_FUNCTION
630             + "  log(null === document.all);\n"
631             + "  log(null == document.all);\n"
632             + "  log(document.all === null);\n"
633             + "  log(document.all == null);\n"
634             + "</script>\n"
635             + "</body></html>";
636 
637         loadPageVerifyTitle2(html);
638     }
639 
640     /**
641      * See https://developer.mozilla.org/en-US/docs/Web/API/HTMLAllCollection#special_type_conversion_behavior.
642      *
643      * @throws Exception if the test fails
644      */
645     @Test
646     @Alerts({"7", "1", "3", "[object HTMLAllCollection]", "5"})
647     public void falsyInBooleanContexts() throws Exception {
648         final String html = DOCTYPE_HTML
649             + "<html>\n"
650             + "<body>\n"
651             + "<script>\n"
652             + LOG_TITLE_FUNCTION
653             + "  x = 11;\n"
654             + "  if(document.all) { x = 1 } else { x = 7 }"
655             + "  log(x);\n"
656 
657             + "  if(!document.all) { x = 1 } else { x = 7 }"
658             + "  log(x);\n"
659 
660             + "  log(document.all ? 4 : 3);\n"
661 
662             + "  log(document.all ?? 'htmlunit');\n"
663             + "  log(document.all?.length);\n"
664             + "</script>\n"
665             + "</body></html>";
666 
667         loadPageVerifyTitle2(html);
668     }
669 
670     /**
671      * See https://developer.mozilla.org/en-US/docs/Web/API/HTMLAllCollection#special_type_conversion_behavior.
672      *
673      * @throws Exception if the test fails
674      */
675     @Test
676     @Alerts("undefined")
677     public void typeof() throws Exception {
678         final String html = DOCTYPE_HTML
679             + "<html>\n"
680             + "<body>\n"
681             + "<script>\n"
682             + LOG_TITLE_FUNCTION
683             + "  log(typeof document.all);\n"
684             + "</script>\n"
685             + "</body></html>";
686 
687         loadPageVerifyTitle2(html);
688     }
689 }