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