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.junit.jupiter.api.Test;
20  
21  /**
22   * Tests for {@link XPathEvaluator}.
23   *
24   * @author Marc Guillemot
25   * @author Chuck Dumont
26   * @author Ronald Brill
27   */
28  public class XPathEvaluatorTest extends WebDriverTestCase {
29  
30      /**
31       * @throws Exception if the test fails
32       */
33      @Test
34      @Alerts({"function", "[object XPathEvaluator]", "[object HTMLHtmlElement]", "first", "second", ""})
35      public void evaluate() throws Exception {
36          final String html = DOCTYPE_HTML
37              + "<html><body>\n"
38              + "<span id='first'>hello</span>\n"
39              + "<div><span id='second'>world</span></div>\n"
40              + "<script>\n"
41              + LOG_TITLE_FUNCTION
42              + "var res = '';\n"
43              + "try {\n"
44              + "  res += typeof window.XPathEvaluator + '§';\n"
45              + "  var xpe = new XPathEvaluator();\n"
46              + "  res += xpe + '§';\n"
47              + "  var nsResolver = xpe.createNSResolver(document.documentElement);\n"
48              + "  res += nsResolver + '§';\n"
49              + "  var result = xpe.evaluate('//span', document.body, nsResolver, 0, null);\n"
50              + "  var found = [];\n"
51              + "  var next;\n"
52              + "  while (next = result.iterateNext()) {\n"
53              + "    res += next.id + '§';\n"
54              + "  }\n"
55              + "} catch(e) { res += 'exception' + '§'; }\n"
56              + "log(res);\n"
57              + "</script></body></html>";
58  
59          loadPageVerifyTitle2(html);
60      }
61  
62      /**
63       * @throws Exception if the test fails
64       */
65      @Test
66      @Alerts({"function", "[object XPathEvaluator]", "[object HTMLHtmlElement]", "first", "second", ""})
67      public void evaluateWithoutResult() throws Exception {
68          final String html = DOCTYPE_HTML
69              + "<html><body>\n"
70              + "<span id='first'>hello</span>\n"
71              + "<div><span id='second'>world</span></div>\n"
72              + "<script>\n"
73              + LOG_TITLE_FUNCTION
74              + "var res = '';\n"
75              + "try {\n"
76              + "  res += typeof window.XPathEvaluator + '§';\n"
77              + "  var xpe = new XPathEvaluator();\n"
78              + "  res += xpe + '§';\n"
79              + "  var nsResolver = xpe.createNSResolver(document.documentElement);\n"
80              + "  res += nsResolver + '§';\n"
81              + "  var result = xpe.evaluate('//span', document.body, nsResolver, 0);\n"
82              + "  var found = [];\n"
83              + "  var next;\n"
84              + "  while (next = result.iterateNext()) {\n"
85              + "    res += next.id + '§';\n"
86              + "  }\n"
87              + "} catch(e) { res += 'exception' + '§'; }\n"
88              + "log(res);\n"
89              + "</script></body></html>";
90  
91          loadPageVerifyTitle2(html);
92      }
93  
94      /**
95       * @throws Exception if the test fails
96       */
97      @Test
98      @Alerts({"function", "[object XPathEvaluator]", "[object HTMLHtmlElement]", "first", "second", ""})
99      public void evaluateWithoutTypeResult() throws Exception {
100         final String html = DOCTYPE_HTML
101             + "<html><body>\n"
102             + "<span id='first'>hello</span>\n"
103             + "<div><span id='second'>world</span></div>\n"
104             + "<script>\n"
105             + LOG_TITLE_FUNCTION
106             + "var res = '';\n"
107             + "try {\n"
108             + "  res += typeof window.XPathEvaluator + '§';\n"
109             + "  var xpe = new XPathEvaluator();\n"
110             + "  res += xpe + '§';\n"
111             + "  var nsResolver = xpe.createNSResolver(document.documentElement);\n"
112             + "  res += nsResolver + '§';\n"
113             + "  var result = xpe.evaluate('//span', document.body, nsResolver);\n"
114             + "  var found = [];\n"
115             + "  var next;\n"
116             + "  while (next = result.iterateNext()) {\n"
117             + "    res += next.id + '§';\n"
118             + "  }\n"
119             + "} catch(e) { res += 'exception' + '§'; }\n"
120             + "log(res);\n"
121             + "</script></body></html>";
122 
123         loadPageVerifyTitle2(html);
124     }
125 
126     /**
127      * @throws Exception if the test fails
128      */
129     @Test
130     @Alerts({"function", "[object XPathEvaluator]", "[object HTMLHtmlElement]", "first", "second", ""})
131     public void evaluateWithoutResolverTypeResult() throws Exception {
132         final String html = DOCTYPE_HTML
133             + "<html><body>\n"
134             + "<span id='first'>hello</span>\n"
135             + "<div><span id='second'>world</span></div>\n"
136             + "<script>\n"
137             + LOG_TITLE_FUNCTION
138             + "var res = '';\n"
139             + "try {\n"
140             + "  res += typeof window.XPathEvaluator + '§';\n"
141             + "  var xpe = new XPathEvaluator();\n"
142             + "  res += xpe + '§';\n"
143             + "  var nsResolver = xpe.createNSResolver(document.documentElement);\n"
144             + "  res += nsResolver + '§';\n"
145             + "  var result = xpe.evaluate('//span', document.body);\n"
146             + "  var found = [];\n"
147             + "  var next;\n"
148             + "  while (next = result.iterateNext()) {\n"
149             + "    res += next.id + '§';\n"
150             + "  }\n"
151             + "} catch(e) { res += 'exception' + '§'; }\n"
152             + "log(res);\n"
153             + "</script></body></html>";
154 
155         loadPageVerifyTitle2(html);
156     }
157 
158     /**
159      * @throws Exception if the test fails
160      */
161     @Test
162     @Alerts("TypeError")
163     public void namespacesWithNodeInArray() throws Exception {
164         final String html = DOCTYPE_HTML
165             + "<html><head>\n"
166             + "<script>\n"
167             + LOG_TITLE_FUNCTION
168             + "  var xml = "
169             + "  '<soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">"
170             + "    <soap:books>"
171             + "      <soap:book>"
172             + "        <title>Immortality</title>"
173             + "        <author>John Smith</author>"
174             + "      </soap:book>"
175             + "    </soap:books>"
176             + "  </soap:Envelope>';\n"
177 
178             + "  function test() {\n"
179             + "    if (window.XPathEvaluator) {\n"
180             + "      var doc = (new DOMParser).parseFromString(xml, 'text/xml');\n"
181             + "      var xpe = new XPathEvaluator();\n"
182             + "      var nsResolver = xpe.createNSResolver(doc.documentElement);\n"
183             + "      try {\n"
184             + "        var result = xpe.evaluate('/soap:Envelope/soap:books/soap:book/title/text()', "
185                                      + "[doc.documentElement], nsResolver, XPathResult.STRING_TYPE, null);\n"
186             + "        log(result.stringValue);\n"
187             + "      } catch(e) { logEx(e); }\n"
188             + "    } else {\n"
189             + "      log('window.XPathEvaluator undefined');\n"
190             + "    }\n"
191             + "  }\n"
192             + "</script></head><body onload='test()'>\n"
193             + "</body></html>";
194 
195         loadPageVerifyTitle2(html);
196     }
197 
198     /**
199      * @throws Exception if the test fails
200      */
201     @Test
202     @Alerts("Immortality")
203     public void namespacesWithCustomNSResolver() throws Exception {
204         final String html = DOCTYPE_HTML
205             + "<html><head>\n"
206             + "<script>\n"
207             + LOG_TITLE_FUNCTION
208             + "  function nsResolver(prefix) {\n"
209             + "    return {s : 'http://schemas.xmlsoap.org/soap/envelope/'}[prefix] || null;\n"
210             + "  }\n"
211 
212             + "  var xml = "
213             + "  '<soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">"
214             + "    <soap:books>"
215             + "      <soap:book>"
216             + "        <title>Immortality</title>"
217             + "        <author>John Smith</author>"
218             + "      </soap:book>"
219             + "    </soap:books>"
220             + "  </soap:Envelope>';\n"
221 
222             + "  function test() {\n"
223             + "    if (window.XPathEvaluator) {\n"
224             + "      var doc = (new DOMParser).parseFromString(xml, 'text/xml');\n"
225             + "      var xpe = new XPathEvaluator();\n"
226             + "      var result = xpe.evaluate('/s:Envelope/s:books/s:book/title/text()', "
227                                    + "doc.documentElement, nsResolver, XPathResult.STRING_TYPE, null);\n"
228             + "      log(result.stringValue);\n"
229             + "    } else {\n"
230             + "      log('window.XPathEvaluator undefined');\n"
231             + "    }\n"
232             + "  }\n"
233             + "</script></head><body onload='test()'>\n"
234             + "</body></html>";
235 
236         loadPageVerifyTitle2(html);
237     }
238 
239     /**
240      * @throws Exception if the test fails
241      */
242     @Test
243     @Alerts("exception")
244     public void createExpressionNoXPath() throws Exception {
245         final String html = DOCTYPE_HTML
246             + "<html><body>\n"
247             + "<script>\n"
248             + LOG_TITLE_FUNCTION
249             + "var res = '';\n"
250             + "try {\n"
251             + "  var expression = new XPathEvaluator().createExpression();\n"
252             + "  res += expression;\n"
253             + "} catch(e) { res += 'exception'; }\n"
254             + "log(res);\n"
255             + "</script></body></html>";
256 
257         loadPageVerifyTitle2(html);
258     }
259 
260     /**
261      * @throws Exception if the test fails
262      */
263     @Test
264     @Alerts({"function", "[object XPathEvaluator]", "[object XPathExpression]§"})
265     public void createExpressionUndefinedXPath() throws Exception {
266         final String html = DOCTYPE_HTML
267                 + "<html><body>\n"
268                 + "<span id='first'>hello</span>\n"
269                 + "<div><span id='second'>world</span></div>\n"
270                 + "<script>\n"
271                 + LOG_TITLE_FUNCTION
272                 + "var res = '';\n"
273                 + "try {\n"
274                 + "  res += typeof window.XPathEvaluator + '§';\n"
275                 + "  var xpe = new XPathEvaluator();\n"
276                 + "  res += xpe + '§';\n"
277                 + "  var expression = xpe.createExpression(undefined);\n"
278                 + "  res += expression + '§';\n"
279                 + "  var result = expression.evaluate(document.body);\n"
280                 + "  var found = [];\n"
281                 + "  var next;\n"
282                 + "  while (next = result.iterateNext()) {\n"
283                 + "    res += next.id + '§';\n"
284                 + "  }\n"
285                 + "} catch(e) { res += 'exception' + e + '§'; }\n"
286                 + "log(res);\n"
287                 + "</script></body></html>";
288 
289         loadPageVerifyTitle2(html);
290     }
291 
292     /**
293      * @throws Exception if the test fails
294      */
295     @Test
296     @Alerts({"function", "[object XPathEvaluator]", "[object XPathExpression]§"})
297     public void createExpressionNullXPath() throws Exception {
298         final String html = DOCTYPE_HTML
299                 + "<html><body>\n"
300                 + "<span id='first'>hello</span>\n"
301                 + "<div><span id='second'>world</span></div>\n"
302                 + "<script>\n"
303                 + LOG_TITLE_FUNCTION
304                 + "var res = '';\n"
305                 + "try {\n"
306                 + "  res += typeof window.XPathEvaluator + '§';\n"
307                 + "  var xpe = new XPathEvaluator();\n"
308                 + "  res += xpe + '§';\n"
309                 + "  var expression = xpe.createExpression(null);\n"
310                 + "  res += expression + '§';\n"
311                 + "  var result = expression.evaluate(document.body);\n"
312                 + "  var found = [];\n"
313                 + "  var next;\n"
314                 + "  while (next = result.iterateNext()) {\n"
315                 + "    res += next.id + '§';\n"
316                 + "  }\n"
317                 + "} catch(e) { res += 'exception' + '§'; }\n"
318                 + "log(res);\n"
319                 + "</script></body></html>";
320 
321         loadPageVerifyTitle2(html);
322     }
323 
324     /**
325      * @throws Exception if the test fails
326      */
327     @Test
328     @Alerts("SyntaxError")
329     public void createExpressionInvalid() throws Exception {
330         final String html = DOCTYPE_HTML
331             + "<html><body>\n"
332             + "<script>\n"
333             + LOG_TITLE_FUNCTION
334             + "var res = '';\n"
335             + "try {\n"
336             + "  var expression = new XPathEvaluator().createExpression('@@');\n"
337             + "  res += expression;\n"
338             + "} catch(e) { res += e.name; }\n"
339             + "log(res);\n"
340             + "</script></body></html>";
341 
342         loadPageVerifyTitle2(html);
343     }
344 }