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