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.dom;
16  
17  import org.htmlunit.WebDriverTestCase;
18  import org.htmlunit.junit.BrowserRunner;
19  import org.htmlunit.junit.annotation.Alerts;
20  import org.htmlunit.util.MimeType;
21  import org.junit.Test;
22  import org.junit.runner.RunWith;
23  
24  /**
25   * Tests for {@link Attr}.
26   *
27   * @author Marc Guillemot
28   * @author Ahmed Ashour
29   * @author Daniel Gredler
30   * @author Ronald Brill
31   * @author Frank Danek
32   */
33  @RunWith(BrowserRunner.class)
34  public class AttrTest extends WebDriverTestCase {
35  
36      /**
37       * @throws Exception if the test fails
38       */
39      @Test
40      @Alerts({"true", "TypeError"})
41      public void specified() throws Exception {
42          final String html = DOCTYPE_HTML
43              + "<html><head><script>\n"
44              + LOG_TITLE_FUNCTION
45              + "function doTest() {\n"
46              + "  try {\n"
47              + "    var s = document.getElementById('testSelect');\n"
48              + "    var o1 = s.options[0];\n"
49              + "    log(o1.getAttributeNode('value').specified);\n"
50              + "    var o2 = s.options[1];\n"
51              + "    log(o2.getAttributeNode('value').specified);\n"
52              + "  } catch(e) { logEx(e); }\n"
53              + "}\n"
54              + "</script></head><body onload='doTest()'>\n"
55              + "<form name='form1'>\n"
56              + "  <select name='select1' id='testSelect'>\n"
57              + "    <option name='option1' value='foo'>One</option>\n"
58              + "    <option>Two</option>\n"
59              + "  </select>\n"
60              + "</form>\n"
61              + "</body></html>";
62  
63          loadPageVerifyTitle2(html);
64      }
65  
66      /**
67       * Trimming of "class" attributes during Firefox emulation was having the unintended side effect
68       * of setting the attribute's "specified" attribute to "false".
69       * @throws Exception if the test fails
70       */
71      @Test
72      @Alerts({"true", "true"})
73      public void specified2() throws Exception {
74          final String html = DOCTYPE_HTML
75              + "<html><body onload='test()'><div id='div' class='test'></div>\n"
76              + "<script>\n"
77              + LOG_TITLE_FUNCTION
78              + "  function test() {\n"
79              + "    var div = document.getElementById('div');\n"
80              + "    log(div.attributes.id.specified);\n"
81              + "    log(div.attributes.class.specified);\n"
82              + "  }\n"
83              + "</script>\n"
84              + "</body></html>";
85          loadPageVerifyTitle2(html);
86      }
87  
88      /**
89       * @throws Exception if the test fails
90       */
91      @Test
92      @Alerts("[object HTMLOptionElement]")
93      public void ownerElement() throws Exception {
94          final String html = DOCTYPE_HTML
95              + "<html><head><script>\n"
96              + LOG_TITLE_FUNCTION
97              + "function doTest() {\n"
98              + "  var s = document.getElementById('testSelect');\n"
99              + "  var o1 = s.options[0];\n"
100             + "  log(o1.getAttributeNode('value').ownerElement);\n"
101             + "}\n"
102             + "</script></head><body onload='doTest()'>\n"
103             + "<form name='form1'>\n"
104             + "  <select name='select1' id='testSelect'>\n"
105             + "    <option name='option1' value='foo'>One</option>\n"
106             + "    <option>Two</option>\n"
107             + "  </select>\n"
108             + "</form>\n"
109             + "</body></html>";
110 
111         loadPageVerifyTitle2(html);
112     }
113 
114     /**
115      * @throws Exception if the test fails
116      */
117     @Test
118     @Alerts({"undefined", "undefined", "undefined"})
119     public void isId() throws Exception {
120         final String html = DOCTYPE_HTML
121             + "<html><head><script>\n"
122             + LOG_TITLE_FUNCTION
123             + "function test() {\n"
124             + "  var d = document.getElementById('d');\n"
125             + "  log(d.getAttributeNode('id').isId);\n"
126             + "  log(d.getAttributeNode('name').isId);\n"
127             + "  log(d.getAttributeNode('width').isId);\n"
128             + "}\n"
129             + "</script></head>\n"
130             + "<body onload='test()'>\n"
131             + "<div iD='d' name='d' width='40'></div>\n"
132             + "</body></html>";
133 
134         loadPageVerifyTitle2(html);
135     }
136 
137     /**
138      * @throws Exception if the test fails
139      */
140     @Test
141     @Alerts({"undefined", "undefined", "undefined", "undefined", "undefined"})
142     public void expando() throws Exception {
143         final String html = DOCTYPE_HTML
144             + "<html><head><script>\n"
145             + LOG_TITLE_FUNCTION
146             + "function test() {\n"
147             + "  var d = document.getElementById('d');\n"
148             + "  log(d.attributes['id'].expando);\n"
149             + "  log(d.attributes['name'].expando);\n"
150             + "  log(d.attributes['style'].expando);\n"
151             + "  log(d.attributes['custom'].expando);\n"
152             + "  log(d.attributes['other'].expando);\n"
153             + "}\n"
154             + "</script></head>\n"
155             + "<body onload='test()'>\n"
156             + "  <div id='d' name='d' style='display: block' custom='value' other></div>\n"
157             + "</body></html>";
158 
159         loadPageVerifyTitle2(html);
160     }
161 
162     /**
163      * Testcase for issue http://sourceforge.net/p/htmlunit/bugs/1493/.
164      * @throws Exception if the test fails
165      */
166     @Test
167     @Alerts("undefined")
168     public void expandoEvent() throws Exception {
169         final String html = DOCTYPE_HTML
170             + "<html><head><script>\n"
171             + LOG_TITLE_FUNCTION
172             + "function test() {\n"
173             + "  var d = document.getElementById('d');\n"
174             + "  d.setAttribute('onfocusin', 't');\n"
175             + "  log(d.attributes['onfocusin'].expando);\n"
176             + "}\n"
177             + "</script></head>\n"
178             + "<body onload='test()'>\n"
179             + "  <div id='d'></div>\n"
180             + "</body></html>";
181 
182         loadPageVerifyTitle2(html);
183     }
184 
185     /**
186      * @throws Exception if the test fails
187      */
188     @Test
189     @Alerts("test()")
190     public void textContent() throws Exception {
191         final String html = DOCTYPE_HTML
192             + "<html><head><script>\n"
193             + LOG_TITLE_FUNCTION
194             + "function test() {\n"
195             + "  var a = document.body.getAttributeNode('onload');\n"
196             + "  log(a.textContent);\n"
197             + "}\n"
198             + "</script></head><body onload='test()'>\n"
199             + "</body></html>";
200 
201         loadPageVerifyTitle2(html);
202     }
203 
204     /**
205      * @throws Exception if the test fails
206      */
207     @Test
208     @Alerts({"null", "null", "null", "null"})
209     public void getAttributeNodeUndefinedAttribute() throws Exception {
210         final String html = DOCTYPE_HTML
211             + "<html><head><script>\n"
212             + LOG_TITLE_FUNCTION
213             + "function test() {\n"
214             + "  var elem = document.getElementById('myDiv');\n"
215             + "  log(elem.getAttributeNode('class'));\n"
216             + "  log(elem.getAttributeNode('style'));\n"
217             + "  log(elem.getAttributeNode('unknown'));\n"
218             + "  log(elem.getAttributeNode('name'));\n"
219             + "}\n"
220             + "</script></head><body onload='test()'>\n"
221             + "<div id='myDiv'></div>\n"
222             + "</body></html>";
223 
224         loadPageVerifyTitle2(html);
225     }
226 
227     /**
228      * @throws Exception if the test fails
229      */
230     @Test
231     @Alerts({"null", "null", "null", "null"})
232     public void getAttributesUndefinedAttribute() throws Exception {
233         final String html = DOCTYPE_HTML
234             + "<html><head><script>\n"
235             + LOG_TITLE_FUNCTION
236             + "function test() {\n"
237             + "  var elem = document.getElementById('myDiv');\n"
238             + "  log(elem.attributes.getNamedItem('class'));\n"
239             + "  log(elem.attributes.getNamedItem('style'));\n"
240             + "  log(elem.attributes.getNamedItem('unknown'));\n"
241             + "  log(elem.attributes.getNamedItem('name'));\n"
242             + "}\n"
243             + "</script></head><body onload='test()'>\n"
244             + "<div id='myDiv'></div>\n"
245             + "</body></html>";
246 
247         loadPageVerifyTitle2(html);
248     }
249 
250     /**
251      * @throws Exception if the test fails
252      */
253     @Test
254     @Alerts({"[object Attr]", "", "[object Attr]", ""})
255     public void value() throws Exception {
256         final String html = DOCTYPE_HTML
257             + "<html><head><script>\n"
258             + LOG_TITLE_FUNCTION
259             + "  function test() {\n"
260             + "    var attr = document.createAttribute('hi');\n"
261             + "    log(attr);\n"
262             + "    log(attr.value);\n"
263             + "    attr = document.implementation.createDocument('', '', null).createAttribute('hi');\n"
264             + "    log(attr);\n"
265             + "    log(attr.value);\n"
266             + "  }\n"
267             + "</script></head><body onload='test()'>\n"
268             + "</body></html>";
269 
270         loadPageVerifyTitle2(html);
271     }
272 
273     /**
274      * @throws Exception if the test fails
275      */
276     @Test
277     @Alerts({"[object Attr]", "undefined"})
278     public void html_baseName() throws Exception {
279         html("baseName");
280     }
281 
282     /**
283      * @throws Exception if the test fails
284      */
285     @Test
286     @Alerts({"[object Attr]", "§§URL§§"})
287     public void html_baseURI() throws Exception {
288         html("baseURI");
289     }
290 
291     /**
292      * @throws Exception if the test fails
293      */
294     @Test
295     @Alerts({"[object Attr]", "null"})
296     public void html_namespaceURI() throws Exception {
297         html("namespaceURI");
298     }
299 
300     /**
301      * @throws Exception if the test fails
302      */
303     @Test
304     @Alerts({"[object Attr]", "testattr"})
305     public void html_localName() throws Exception {
306         html("localName");
307     }
308 
309     /**
310      * @throws Exception if the test fails
311      */
312     @Test
313     @Alerts({"[object Attr]", "null"})
314     public void html_prefix() throws Exception {
315         html("prefix");
316     }
317 
318     private void html(final String methodName) throws Exception {
319         final String html = DOCTYPE_HTML
320             + "<html>\n"
321             + "<script>\n"
322             + LOG_TITLE_FUNCTION
323             + "  function test() {\n"
324             + "    debug(document.getElementById('tester').attributes.getNamedItem('testAttr'));\n"
325             + "  }\n"
326             + "  function debug(e) {\n"
327             + "    log(e);\n"
328             + "    log(e." + methodName + ");\n"
329             + "  }\n"
330             + "</script>\n"
331             + "</head>\n"
332             + "<body onload='test()'>\n"
333             + "<div id='tester' testAttr='test'></div>\n"
334             + "</body></html>";
335 
336         expandExpectedAlertsVariables(URL_FIRST);
337         loadPageVerifyTitle2(html);
338     }
339 
340     /**
341      * @throws Exception if the test fails
342      */
343     @Test
344     @Alerts({"[object Attr]", "undefined"})
345     public void xml_baseName() throws Exception {
346         xml("baseName");
347     }
348 
349     /**
350      * @throws Exception if the test fails
351      */
352     @Test
353     @Alerts({"[object Attr]", "§§URL§§foo.xml"})
354     public void xml_baseURI() throws Exception {
355         expandExpectedAlertsVariables(URL_FIRST);
356         xml("baseURI");
357     }
358 
359     /**
360      * @throws Exception if the test fails
361      */
362     @Test
363     @Alerts({"[object Attr]", "null"})
364     public void xml_namespaceURI() throws Exception {
365         xml("namespaceURI");
366     }
367 
368     /**
369      * @throws Exception if the test fails
370      */
371     @Test
372     @Alerts({"[object Attr]", "testAttr"})
373     public void xml_localName() throws Exception {
374         xml("localName");
375     }
376 
377     /**
378      * @throws Exception if the test fails
379      */
380     @Test
381     @Alerts({"[object Attr]", "null"})
382     public void xml_prefix() throws Exception {
383         xml("prefix");
384     }
385 
386     private void xml(final String methodName) throws Exception {
387         final String html = DOCTYPE_HTML
388             + "<html>\n"
389             + "  <head>\n"
390             + "    <script>\n"
391             + LOG_TITLE_FUNCTION
392             + "      function test() {\n"
393             + "        var request;\n"
394             + "        request = new XMLHttpRequest();\n"
395             + "        request.open('GET', 'foo.xml', false);\n"
396             + "        request.send('');\n"
397             + "        var doc = request.responseXML;\n"
398             + "        debug(doc.documentElement.childNodes[0].attributes.item(0));\n"
399             + "      }\n"
400             + "      function debug(e) {\n"
401             + "        try {\n"
402             + "          log(e);\n"
403             + "        } catch(ex) {log(ex)}\n"
404             + "        log(e." + methodName + ");\n"
405             + "      }\n"
406             + "    </script>\n"
407             + "  </head>\n"
408             + "  <body onload='test()'>\n"
409             + "  </body>\n"
410             + "</html>";
411 
412         final String xml =
413               "<xml>"
414             + "<div testAttr='test'></div>"
415             + "</xml>";
416 
417         getMockWebConnection().setDefaultResponse(xml, MimeType.TEXT_XML);
418         loadPageVerifyTitle2(html);
419     }
420 }