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