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;
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 Reflect.
23   *
24   * @author Ronald Brill
25   * @author Lai Quang Duong
26   */
27  public class ReflectTest extends WebDriverTestCase {
28  
29      /**
30       * @throws Exception if the test fails
31       */
32      @Test
33      @Alerts("[object Reflect]")
34      public void testToString() throws Exception {
35          test("log(Reflect.toString())");
36      }
37  
38      /**
39       * @throws Exception if the test fails
40       */
41      @Test
42      @Alerts("1")
43      public void apply() throws Exception {
44          test("log(Reflect.apply(Math.floor, undefined, [1.75]))");
45      }
46  
47      /**
48       * @throws Exception if the test fails
49       */
50      @Test
51      @Alerts({"1", "true", "4", "arg1", "2", "undefined", "null"})
52      public void applyDetails() throws Exception {
53          final String js =
54                  "var o = {};\n"
55                  + "var count = 0;\n"
56                  + "var results, args;\n"
57  
58                  + "function fn() {\n"
59                  + "  count++;\n"
60                  + "  results = {\n"
61                  + "    thisArg: this,\n"
62                  + "    args: arguments\n"
63                  + "  };\n"
64                  + "}\n"
65  
66                  + "Reflect.apply(fn, o, ['arg1', 2, , null]);\n"
67  
68                  + "log(count);\n"
69                  + "log(results.thisArg === o);\n"
70                  + "log(results.args.length);\n"
71                  + "log(results.args[0]);\n"
72                  + "log(results.args[1]);\n"
73                  + "log(results.args[2]);\n"
74                  + "log(results.args[3]);\n";
75          test(js);
76      }
77  
78      /**
79       * @throws Exception if the test fails
80       */
81      @Test
82      @Alerts("TypeError")
83      public void applyMissingArgs() throws Exception {
84          final String js =
85                  "try {\n"
86                  + "  Reflect.apply();\n"
87                  + "} catch(e) {\n"
88                  + "  logEx(e);\n"
89                  + "}";
90          test(js);
91      }
92  
93      /**
94       * @throws Exception if the test fails
95       */
96      @Test
97      @Alerts("TypeError")
98      public void applyTargetNotFunction() throws Exception {
99          final String js =
100                 "try {\n"
101                 + "  Reflect.apply({}, undefined, [1.75]);\n"
102                 + "} catch(e) {\n"
103                 + "  logEx(e);\n"
104                 + "}";
105         test(js);
106     }
107 
108     /**
109      * @throws Exception if the test fails
110      */
111     @Test
112     @Alerts("TypeError")
113     public void applyArgumentsListNotFunction() throws Exception {
114         final String js =
115                 "var s1 = Symbol('1');"
116                 + "try {\n"
117                 + "  Reflect.apply(Math.floor, undefined, s1);\n"
118                 + "} catch(e) {\n"
119                 + "  logEx(e);\n"
120                 + "}";
121         test(js);
122     }
123 
124     /**
125      * @throws Exception if the test fails
126      */
127     @Test
128     @Alerts({"true", "1776"})
129     public void construct() throws Exception {
130         final String js =
131                 "var d = Reflect.construct(Date, [1776, 6, 4]);\n"
132                 + "log(d instanceof Date);\n"
133                 + "log(d.getFullYear());";
134         test(js);
135     }
136 
137     /**
138      * @throws Exception if the test fails
139      */
140     @Test
141     @Alerts({"true", "42"})
142     public void defineProperty() throws Exception {
143         final String js =
144                 "var o = {};\n"
145 
146                 + "log(Reflect.defineProperty(o, 'p', { value: 42 }));\n"
147                 + "log(o.p);";
148         test(js);
149     }
150 
151     /**
152      * @throws Exception if the test fails
153      */
154     @Test
155     @Alerts({"true", "true", "undefined"})
156     public void definePropertyWithoutValue() throws Exception {
157         final String js =
158                 "var o = {};\n"
159 
160                 + "log(Reflect.defineProperty(o, 'p', {}));\n"
161                 + "log(Reflect.has(o, 'p'));\n"
162                 + "log(o.p);";
163 
164         test(js);
165     }
166 
167     /**
168      * @throws Exception if the test fails
169      */
170     @Test
171     @Alerts({"false", "undefined"})
172     public void definePropertyFreezed() throws Exception {
173         final String js =
174                 "var o = {};\n"
175                 + "Object.freeze(o);\n"
176 
177                 + "log(Reflect.defineProperty(o, 'p', { value: 42 }));\n"
178                 + "log(o.p);";
179         test(js);
180     }
181 
182     /**
183      * @throws Exception if the test fails
184      */
185     @Test
186     @Alerts({"[get,set,enumerable,configurable]", "false", "true", "true", "true"})
187     public void getOwnPropertyDescriptor() throws Exception {
188         final String js =
189                 "var o1 = {};\n"
190                 + "var fn = function() {};\n"
191                 + "Object.defineProperty(o1, 'p', {\n"
192                 + "  get: fn,\n"
193                 + "  configurable: true\n"
194                 + "});\n"
195 
196                 + "var result = Reflect.getOwnPropertyDescriptor(o1, 'p');\n"
197 
198                 + "log('[' + Object.getOwnPropertyNames(result) + ']');\n"
199                 + "log(result.enumerable);\n"
200                 + "log(result.configurable);\n"
201                 + "log(result.get === fn);\n"
202                 + "log(result.set === undefined);";
203         test(js);
204     }
205 
206     /**
207      * @throws Exception if the test fails
208      */
209     @Test
210     @Alerts({"true", "false", "false"})
211     public void isExtensible() throws Exception {
212         final String js =
213                 "var o1 = {};\n"
214 
215                 + "log(Reflect.isExtensible(o1));\n"
216 
217                 + "Reflect.preventExtensions(o1);\n"
218                 + "log(Reflect.isExtensible(o1));\n"
219 
220                 + "var o2 = Object.seal({});\n"
221                 + "log(Reflect.isExtensible(o2));\n";
222 
223         test(js);
224     }
225 
226     /**
227      * @throws Exception if the test fails
228      */
229     @Test
230     @Alerts({"p1,p2", "length"})
231     public void ownKeys() throws Exception {
232         final String js =
233                 "var o1 = {\n"
234                 + "  p1: 42,\n"
235                 + "  p2: 'one'\n"
236                 + "};\n"
237 
238                 + "var a1 = [];\n"
239 
240                 + "log(Reflect.ownKeys(o1));\n"
241                 + "log(Reflect.ownKeys(a1));";
242         test(js);
243     }
244 
245     /**
246      * @throws Exception if the test fails
247      */
248     @Test
249     @Alerts({"0", "6", "8", "55", "773", "str", "-1", "str2", "Symbol(foo)", "Symbol(bar)"})
250     public void ownKeys2() throws Exception {
251         final String js =
252                 "    var obj = {};\n"
253                 + "    var s1 = Symbol.for('foo');\n"
254                 + "    var s2 = Symbol.for('bar');\n"
255 
256                 + "    obj[s1] = 0;\n"
257                 + "    obj['str'] = 0;\n"
258                 + "    obj[773] = 0;\n"
259                 + "    obj['55'] = 0;\n"
260                 + "    obj[0] = 0;\n"
261                 + "    obj['-1'] = 0;\n"
262                 + "    obj[8] = 0;\n"
263                 + "    obj['6'] = 0;\n"
264                 + "    obj[s2] = 0;\n"
265                 + "    obj['str2'] = 0;\n"
266 
267                 + "    for (const key of Reflect.ownKeys(obj)){\n"
268                 + "      log(String(key));\n;"
269                 + "    }\n";
270 
271         test(js);
272     }
273 
274     /**
275      * @throws Exception if the test fails
276      */
277     @Test
278     @Alerts("0")
279     public void ownKeysEmptyObj() throws Exception {
280         final String js =
281                 "log(Reflect.ownKeys({}).length)";
282         test(js);
283     }
284 
285     /**
286      * @throws Exception if the test fails
287      */
288     @Test
289     @Alerts("0")
290     public void ownKeysDeleteObj() throws Exception {
291         final String js =
292                 "var o = { d: 42 };\n"
293                 + "delete o.d;\n"
294                 + "log(Reflect.ownKeys(o).length);";
295         test(js);
296     }
297 
298     /**
299      * @throws Exception if the test fails
300      */
301     @Test
302     @Alerts("length")
303     public void ownKeysEmptyArray() throws Exception {
304         final String js =
305                 "log(Reflect.ownKeys([]));";
306         test(js);
307     }
308 
309     /**
310      * @throws Exception if the test fails
311      */
312     @Test
313     @Alerts("2,length")
314     public void ownKeysArray() throws Exception {
315         final String js =
316                 "log(Reflect.ownKeys([, , 2]));";
317         test(js);
318     }
319 
320     /**
321      * @throws Exception if the test fails
322      */
323     @Test
324     @Alerts("p1,p2")
325     public void ownKeysNotEnumerable() throws Exception {
326         final String js =
327                 "var o = {};\n"
328                 + "Object.defineProperty(o, 'p1', { value: 42, enumerable: false });\n"
329                 + "Object.defineProperty(o, 'p2', { get: function() {}, enumerable: false });\n"
330 
331                 + "log(Reflect.ownKeys(o));";
332         test(js);
333     }
334 
335     /**
336      * @throws Exception if the test fails
337      */
338     @Test
339     @Alerts({"true", "false", "true"})
340     public void has() throws Exception {
341         final String js =
342                 "var o1 = { p: 42 }\n"
343                 + "log(Reflect.has(o1, 'p'));\n"
344                 + "log(Reflect.has(o1, 'p2'));\n"
345                 + "log(Reflect.has(o1, 'toString'));";
346         test(js);
347     }
348 
349     /**
350      * @throws Exception if the test fails
351      */
352     @Test
353     @Alerts({"function () { [native code] }", "true", "false", "true", "true"})
354     public void has2() throws Exception {
355         final String js =
356             "    log(Reflect.has.__proto__);\n"
357 
358             + "    log(Reflect.has({x: 0}, 'x'));\n"
359             + "    log(Reflect.has({x: 0}, 'y'));\n"
360             + "    log(Reflect.has({x: 0}, 'toString'));\n"
361 
362             + "    log((Reflect ? Reflect.has : log)({x: 0}, 'x'));\n";
363 
364         test(js);
365     }
366 
367     /**
368      * @throws Exception if the test fails
369      */
370     @Test
371     @Alerts({"true", "false"})
372     public void hasSymbol() throws Exception {
373         final String js =
374                 "var s1 = Symbol('1');\n"
375                 + "var s2 = Symbol('1');\n"
376                 + "var o = {};\n"
377                 + "o[s1] = 42;\n"
378 
379                 + "log(Reflect.has(o, s1));\n"
380                 + "log(Reflect.has(o, 2));";
381         test(js);
382     }
383 
384     /**
385      * @throws Exception if the test fails
386      */
387     @Test
388     @Alerts("function")
389     public void hasProto() throws Exception {
390         final String js =
391                 "var o1 = { p: 42 }\n"
392                 + "log(typeof Reflect.has.__proto__);";
393         test(js);
394     }
395 
396     /**
397      * @throws Exception if the test fails
398      */
399     @Test
400     @Alerts({"42", "true", "true", "true"})
401     public void getOwnPropertyDescriptorSymbol() throws Exception {
402         final String js =
403                 "var s = Symbol('sym');\n"
404                 + "var o = {};\n"
405                 + "o[s] = 42;\n"
406 
407                 + "var result = Reflect.getOwnPropertyDescriptor(o, s);\n"
408 
409                 + "log(result.value);\n"
410                 + "log(result.enumerable);\n"
411                 + "log(result.configurable);\n"
412                 + "log(result.writable);";
413         test(js);
414     }
415 
416     /**
417      * @throws Exception if the test fails
418      */
419     @Test
420     @Alerts("true")
421     public void getOwnPropertyDescriptorUndefinedProperty() throws Exception {
422         final String js =
423                 "var o = Object.create({p: 1});\n"
424                 + "log(undefined == Reflect.getOwnPropertyDescriptor(o, 'p'));\n";
425         test(js);
426     }
427 
428     /**
429      * @throws Exception if the test fails
430      */
431     @Test
432     @Alerts("one")
433     public void getPropertyByInt() throws Exception {
434         final String js =
435                 "var a = ['zero', 'one']\n"
436                 + "log(Reflect.get(a, 1));";
437         test(js);
438     }
439 
440 
441     /**
442      * @throws Exception if the test fails
443      */
444     @Test
445     @Alerts({"value 1", "undefined", "foo", "42", "undefined"})
446     public void getProperty() throws Exception {
447         final String js =
448                 "var o = {};\n"
449                 + "o.p1 = 'value 1';\n"
450 
451                 + "log(Reflect.get(o, 'p1'));\n"
452 
453                 + "Object.defineProperty(o, 'p2', { get: undefined });\n"
454                 + "log(Reflect.get(o, 'p2'));\n"
455 
456                 + "Object.defineProperty(o, 'p3', { get: function() { return 'foo'; } });\n"
457                 + "log(Reflect.get(o, 'p3'));\n"
458 
459                 + "var o2 = Object.create({ p: 42 });\n"
460                 + "log(Reflect.get(o2, 'p'));\n"
461 
462                 + "log(Reflect.get(o2, 'u'));";
463 
464         test(js);
465     }
466 
467     /**
468      * @throws Exception if the test fails
469      */
470     @Test
471     @Alerts({"true", "true", "false"})
472     public void setPrototypeOf() throws Exception {
473         final String js =
474                 "var o1 = {};\n"
475 
476                 + "log(Reflect.setPrototypeOf(o1, Object.prototype));\n"
477                 + "log(Reflect.setPrototypeOf(o1, null));\n"
478 
479                 + "var o2 = {};\n"
480 
481                 + "log(Reflect.setPrototypeOf(Object.freeze(o2), null));";
482         test(js);
483     }
484 
485     /**
486      * @throws Exception if the test fails
487      */
488     @Test
489     @Alerts("false")
490     public void setPrototypeOfCycle() throws Exception {
491         final String js =
492                 "var o1 = {};\n"
493                 + "log(Reflect.setPrototypeOf(o1, o1));";
494         test(js);
495     }
496 
497     /**
498      * @throws Exception if the test fails
499      */
500     @Test
501     @Alerts({"true", "true", "false"})
502     public void setPrototypeOfCycleComplex() throws Exception {
503         final String js =
504                 "var o1 = {};\n"
505                 + "var o2 = {};\n"
506                 + "var o3 = {};\n"
507 
508                 + "log(Reflect.setPrototypeOf(o1, o2));\n"
509                 + "log(Reflect.setPrototypeOf(o2, o3));\n"
510                 + "log(Reflect.setPrototypeOf(o3, o1));";
511         test(js);
512     }
513 
514     /**
515      * @throws Exception if the test fails
516      */
517     @Test
518     @Alerts({"true", "true", "true"})
519     public void setPrototypeOfSame() throws Exception {
520         final String js =
521                 "var o1 = {};\n"
522                 + "Object.preventExtensions(o1);\n"
523 
524                 + "var o2 = Object.create(null);\n"
525                 + "Object.preventExtensions(o2);\n"
526 
527                 + "var proto = {};\n"
528                 + "var o3 = Object.create(proto);\n"
529                 + "Object.preventExtensions(o3);\n"
530 
531                 + "log(Reflect.setPrototypeOf(o1, Object.prototype));\n"
532                 + "log(Reflect.setPrototypeOf(o2, null));\n"
533                 + "log(Reflect.setPrototypeOf(o3, proto));";
534         test(js);
535     }
536 
537     private void test(final String js) throws Exception {
538         final String html = DOCTYPE_HTML
539                 + "<html><head>\n"
540                 + "<body>\n"
541                 + "<script>\n"
542                 + LOG_TITLE_FUNCTION
543                 + "  if (typeof Reflect != 'undefined') {\n"
544                 + js
545                 + "  } else { log('no Reflect'); }\n"
546                 + "</script>\n"
547                 + "</body></html>";
548 
549         loadPageVerifyTitle2(html);
550     }
551 }