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;
16  
17  import org.htmlunit.WebDriverTestCase;
18  import org.htmlunit.junit.annotation.Alerts;
19  import org.htmlunit.junit.annotation.HtmlUnitNYI;
20  import org.junit.jupiter.api.Test;
21  import org.openqa.selenium.WebDriverException;
22  
23  /**
24   * Tests for Functions.
25   *
26   * @author Ronald Brill
27   */
28  public class FunctionsTest extends WebDriverTestCase {
29  
30      /**
31       * @throws Exception if the test fails
32       */
33      @Test
34      @Alerts("function\\sfoo()\\s{\\s\\s\\s\\sreturn\\s\\t'a'\\s+\\s'b'\\s}")
35      public void function_toString() throws Exception {
36          final String html = DOCTYPE_HTML
37              + "<html><head>\n"
38              + "<script>\n"
39              + LOG_TITLE_FUNCTION_NORMALIZE
40              + "function test() {\n"
41              + "  function foo() {    return \t'a' + 'b' };\n"
42              + "  log(foo.toString());\n"
43              + "}\n"
44              + "</script></head>\n"
45              + "<body onload='test()'>\n"
46              + "</body></html>";
47  
48          loadPageVerifyTitle2(html);
49      }
50  
51      /**
52       * @throws Exception if the test fails
53       */
54      @Test
55      @Alerts("function\\sfoo()\\s\\n{\\s\\n\\sreturn\\s\\t'x'\\s\\n\\n}")
56      public void function_toStringNewLines() throws Exception {
57          final String html = DOCTYPE_HTML
58              + "<html><head>\n"
59              + "<script>\n"
60              + LOG_TITLE_FUNCTION_NORMALIZE
61              + "function test() {\n"
62              + "  function foo() \n{ \r\n return \t'x' \n\n};\n"
63              + "  log(foo.toString());\n"
64              + "}\n"
65              + "</script></head>\n"
66              + "<body onload='test()'>\n"
67              + "</body></html>";
68  
69          loadPageVerifyTitle2(html);
70      }
71  
72      /**
73       * @throws Exception if the test fails
74       */
75      @Test
76      @Alerts("()\\s=>\\s{\\s\\nreturn\\s\\s'=>'\\s\\s\\s}")
77      public void arrowFunction_toString() throws Exception {
78          final String html = DOCTYPE_HTML
79              + "<html><head>\n"
80              + "<script>\n"
81              + LOG_TITLE_FUNCTION_NORMALIZE
82              + "function test() {\n"
83              + "  var foo = () => { \nreturn  '=>'   };"
84              + "  log(foo.toString());\n"
85              + "}\n"
86              + "</script></head>\n"
87              + "<body onload='test()'>\n"
88              + "</body></html>";
89  
90          loadPageVerifyTitle2(html);
91      }
92  
93      /**
94       * @throws Exception if the test fails
95       */
96      @Test
97      @Alerts({"function()\\s{\\n\\s\\s\\s\\s\\s\\sreturn\\s'X';\\n\\s\\s\\s\\s}",
98               "function()\\s{\\n\\s\\s\\s\\s\\s\\sreturn\\s'X';\\n\\s\\s\\s\\s}"})
99      public void boundFunction_toString() throws Exception {
100         final String html = DOCTYPE_HTML
101             + "<html><head>\n"
102             + "<script>\n"
103             + LOG_TITLE_FUNCTION_NORMALIZE
104             + "function test() {\n"
105             + "  var oobj = {\n"
106             + "    getX: function() {\n"
107             + "      return 'X';\n"
108             + "    }\n"
109             + "  };\n"
110 
111             + "  log(oobj.getX.toString());\n"
112 
113             + "  var boundGetX = oobj.getX.bind(oobj);"
114             + "  log(oobj.getX.toString());\n"
115             + "}\n"
116             + "</script></head>\n"
117             + "<body onload='test()'>\n"
118             + "</body></html>";
119 
120         loadPageVerifyTitle2(html);
121     }
122 
123     /**
124      * @throws Exception if the test fails
125      */
126     @Test
127     @Alerts({"foo = undefined", "1"})
128     @HtmlUnitNYI(CHROME = "org.htmlunit.ScriptException: ReferenceError: \"foo\" is not defined.",
129             EDGE = "org.htmlunit.ScriptException: ReferenceError: \"foo\" is not defined.",
130             FF = "org.htmlunit.ScriptException: ReferenceError: \"foo\" is not defined.",
131             FF_ESR = "org.htmlunit.ScriptException: ReferenceError: \"foo\" is not defined.")
132     public void conditionallyCreatedFunction() throws Exception {
133         final String html = DOCTYPE_HTML
134             + "<html><head></head>\n"
135             + "<body>\n"
136             + "<script>\n"
137             + LOG_TITLE_FUNCTION
138             + "  log('foo = ' + foo);\n"
139             + "  if (true) {\n"
140             + "    log(foo());\n"
141             + "    function foo() { return 1; }\n"
142             + "  }\n"
143             + "</script>\n"
144             + "</body></html>";
145 
146         try {
147             loadPageVerifyTitle2(html);
148         }
149         catch (final WebDriverException e) {
150             assertTrue(e.getMessage(), e.getMessage().startsWith(getExpectedAlerts()[0]));
151         }
152     }
153 
154     /**
155      * @throws Exception if the test fails
156      */
157     @Test
158     @Alerts({"ReferenceError", "1"})
159     public void conditionallyCreatedFunctionStrict() throws Exception {
160         final String html = DOCTYPE_HTML
161             + "<html><head></head>\n"
162             + "<body>\n"
163             + "<script>\n"
164             + "  'use strict';\n"
165             + LOG_TITLE_FUNCTION
166             + "  try {\n"
167             + "    log('foo = ' + foo);\n"
168             + "  } catch(e) { logEx(e); }\n"
169             + "  if (true) {\n"
170             + "    log(foo());\n"
171             + "    function foo() { return 1; }\n"
172             + "  }\n"
173             + "</script>\n"
174             + "</body></html>";
175 
176         loadPageVerifyTitle2(html);
177     }
178 
179     /**
180      * @throws Exception if the test fails
181      */
182     @Test
183     @Alerts("Hello!")
184     public void applyThisFromBoundArgs() throws Exception {
185         final String html = DOCTYPE_HTML
186                 + "<html><head></head>\n"
187                 + "<body>\n"
188                 + "<script>\n"
189                 + "  'use strict';\n"
190                 + LOG_TITLE_FUNCTION
191                 + "  var f = function(x) { return this.toString(); };\n"
192                 + "  var a = f.apply;\n"
193                 + "  var b = a.bind(f, 'Hello!');\n"
194                 + "  log(b([1,2]));\n"
195                 + "</script>\n"
196                 + "</body></html>";
197 
198         loadPageVerifyTitle2(html);
199     }
200 
201     /**
202      * @throws Exception if the test fails
203      */
204     @Test
205     @Alerts("Hello!")
206     public void applyToApplyCallsCorrectFunction() throws Exception {
207         final String html = DOCTYPE_HTML
208                 + "<html><head></head>\n"
209                 + "<body>\n"
210                 + "<script>\n"
211                 + "  'use strict';\n"
212                 + LOG_TITLE_FUNCTION
213                 + "  function foo(x) {return x;};\n"
214                 + "  var r = Function.prototype.apply.apply(foo, ['b', ['Hello!', 'Goodbye!']]);\n"
215                 + "  log(r);\n"
216                 + "</script>\n"
217                 + "</body></html>";
218 
219         loadPageVerifyTitle2(html);
220     }
221 
222     /**
223      * @throws Exception if the test fails
224      */
225     @Test
226     @Alerts("b")
227     public void applyToApplySetsCorrectFunctionThis() throws Exception {
228         final String html = DOCTYPE_HTML
229                 + "<html><head></head>\n"
230                 + "<body>\n"
231                 + "<script>\n"
232                 + "  'use strict';\n"
233                 + LOG_TITLE_FUNCTION
234                 + "  function foo(x) {return this.toString();};\n"
235                 + "  var r = Function.prototype.apply.apply(foo, ['b', ['Hello!', 'Goodbye!']]);\n"
236                 + "  log(r);\n"
237                 + "</script>\n"
238                 + "</body></html>";
239 
240         loadPageVerifyTitle2(html);
241     }
242 
243     /**
244      * @throws Exception if the test fails
245      */
246     @Test
247     @Alerts("Hello!")
248     public void applyToCallCallsCorrectFunction() throws Exception {
249         final String html = DOCTYPE_HTML
250                 + "<html><head></head>\n"
251                 + "<body>\n"
252                 + "<script>\n"
253                 + "  'use strict';\n"
254                 + LOG_TITLE_FUNCTION
255                 + "  function foo(x) {return x;};\n"
256                 + "  var r = foo.call.apply(foo, ['b', 'Hello!']);\n"
257                 + "  log(r);\n"
258                 + "</script>\n"
259                 + "</body></html>";
260 
261         loadPageVerifyTitle2(html);
262     }
263 
264     /**
265      * @throws Exception if the test fails
266      */
267     @Test
268     @Alerts("b")
269     public void applyToCallSetsCorrectFunctionThis() throws Exception {
270         final String html = DOCTYPE_HTML
271                 + "<html><head></head>\n"
272                 + "<body>\n"
273                 + "<script>\n"
274                 + "  'use strict';\n"
275                 + LOG_TITLE_FUNCTION
276                 + "  function foo(x) {return this.toString();};\n"
277                 + "  var r = foo.call.apply(foo, ['b', 'Hello!']);\n"
278                 + "  log(r);\n"
279                 + "</script>\n"
280                 + "</body></html>";
281 
282         loadPageVerifyTitle2(html);
283     }
284 
285     /**
286      * @throws Exception if the test fails
287      */
288     @Test
289     @Alerts("abcd / undefined")
290     public void boundWithoutArgs() throws Exception {
291         final String html = DOCTYPE_HTML
292                 + "<html><head></head>\n"
293                 + "<body>\n"
294                 + "<script>\n"
295                 + LOG_TITLE_FUNCTION
296                 + "  function foo(x) { return this.toString() + ' / ' + x; };\n"
297                 + "  var boundThis = 'abcd';\n"
298                 + "  var boundFoo = Function.prototype.bind.call(foo, boundThis);\n"
299                 + "  var r = boundFoo.call('Hello!');\n"
300                 + "  log(r);\n"
301                 + "</script>\n"
302                 + "</body></html>";
303 
304         loadPageVerifyTitle2(html);
305     }
306 
307     /**
308      * @throws Exception if the test fails
309      */
310     @Test
311     @Alerts("abcd / world")
312     public void boundWithArg() throws Exception {
313         final String html = DOCTYPE_HTML
314                 + "<html><head></head>\n"
315                 + "<body>\n"
316                 + "<script>\n"
317                 + LOG_TITLE_FUNCTION
318                 + "  function foo(x) { return this.toString() + ' / ' + x; };\n"
319                 + "  var boundThis = 'abcd';\n"
320                 + "  var boundFoo = Function.prototype.bind.call(foo, boundThis, 'world');\n"
321                 + "  var r = boundFoo.call('Hello!');\n"
322                 + "  log(r);\n"
323                 + "</script>\n"
324                 + "</body></html>";
325 
326         loadPageVerifyTitle2(html);
327     }
328 
329     /**
330      * @throws Exception if the test fails
331      */
332     @Test
333     @Alerts("world")
334     public void bound() throws Exception {
335         final String html = DOCTYPE_HTML
336                 + "<html><head></head>\n"
337                 + "<body>\n"
338                 + "<script>\n"
339                 + LOG_TITLE_FUNCTION
340                 + "  function foo() { return 'world'; };\n"
341                 + "  var boundThis = 'abcd';\n"
342                 + "  var boundFoo = Function.prototype.bind.call(foo, boundThis);\n"
343                 + "  var r = boundFoo.call(' dd');\n"
344                 + "  log(r);\n"
345                 + "</script>\n"
346                 + "</body></html>";
347 
348         loadPageVerifyTitle2(html);
349     }
350 
351     /**
352      * @throws Exception if the test fails
353      */
354     @Test
355     @Alerts({"f1", "f2", "f3", "f4", "f5",
356              "f6", "g", "3", "[foo]", ""})
357     public void name() throws Exception {
358         final String html = DOCTYPE_HTML
359                 + "<html><head></head>\n"
360                 + "<body>\n"
361                 + "<script>\n"
362                 + LOG_TITLE_FUNCTION
363 
364                 + "let f1 = function() {}; log(f1.name);\n"
365                 + "let f2= () => {}; log(f2.name);\n"
366                 + "let f3 = function() {}; log(f3.name);\n"
367                 + "let f4 = () => {}; log(f4.name);\n"
368                 + "let f5 = function() {}; log(f5.name);\n"
369 
370                 + "let f6 = () => {}; log(f6.name);\n"
371                 + "let f7 = function g() {}; log(f7.name);\n"
372                 + "let f8 = { [1 + 2]() {} }; log(f8['3'].name);\n"
373                 + "let s = Symbol('foo'); let f9 = { [s]() {} }; log(f9[s].name);\n"
374                 + "let s2 = Symbol(); f10 = { [s2]() {} }; log(f10[s2].name);\n"
375 
376                 + "</script>\n"
377                 + "</body></html>";
378 
379         loadPageVerifyTitle2(html);
380     }
381 }