reflect.js 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679
  1. var arePropertyDescriptorsSupported = function () {
  2. try {
  3. Object.defineProperty({}, 'x', {});
  4. return true;
  5. } catch (e) { /* this is IE 8. */
  6. return false;
  7. }
  8. };
  9. var supportsDescriptors = !!Object.defineProperty && arePropertyDescriptorsSupported();
  10. var functionsHaveNames = function f() {}.name === 'f';
  11. var hasSymbols = typeof Symbol === 'function' && typeof Symbol() === 'symbol';
  12. var ifSymbolsIt = hasSymbols ? it : xit;
  13. var describeIfGetProto = Object.getPrototypeOf ? describe : xdescribe;
  14. var describeIfSetProto = Object.setPrototypeOf ? describe : xdescribe;
  15. var describeIfES5 = supportsDescriptors ? describe : xdescribe;
  16. var describeIfExtensionsPreventible = Object.preventExtensions ? describe : xdescribe;
  17. var describeIfGetOwnPropertyNames = Object.getOwnPropertyNames ? describe : xdescribe;
  18. var ifExtensionsPreventibleIt = Object.preventExtensions ? it : xit;
  19. var ifES5It = supportsDescriptors ? it : xit;
  20. var ifFreezeIt = typeof Object.freeze === 'function' ? it : xit;
  21. var ifFunctionsHaveNamesIt = functionsHaveNames ? it : xit;
  22. var ifShimIt = (typeof process !== 'undefined' && process.env.NO_ES6_SHIM) ? it.skip : it;
  23. describe('Reflect', function () {
  24. if (typeof Reflect === 'undefined') {
  25. return it('exists', function () {
  26. expect(this).to.have.property('Reflect');
  27. });
  28. }
  29. var object = {
  30. something: 1,
  31. _value: 0
  32. };
  33. if (supportsDescriptors) {
  34. /* eslint-disable accessor-pairs */
  35. Object.defineProperties(object, {
  36. value: {
  37. get: function () {
  38. return this._value;
  39. }
  40. },
  41. setter: {
  42. set: function (val) {
  43. this._value = val;
  44. }
  45. },
  46. bool: {
  47. value: true
  48. }
  49. });
  50. /* eslint-enable accessor-pairs */
  51. }
  52. var testXThrow = function (values, func) {
  53. var checker = function checker(item) {
  54. try {
  55. func(item);
  56. return false;
  57. } catch (e) {
  58. return e instanceof TypeError;
  59. }
  60. };
  61. values.forEach(function (item) {
  62. expect(item).to.satisfy(checker);
  63. });
  64. };
  65. var testCallableThrow = testXThrow.bind(null, [null, undefined, 1, 'string', true, [], {}]);
  66. var testPrimitiveThrow = testXThrow.bind(null, [null, undefined, 1, 'string', true]);
  67. ifShimIt('is on the exported object', function () {
  68. var exported = require('../');
  69. expect(exported.Reflect).to.equal(Reflect);
  70. });
  71. describe('.apply()', function () {
  72. if (typeof Reflect.apply === 'undefined') {
  73. return it('exists', function () {
  74. expect(Reflect).to.have.property('apply');
  75. });
  76. }
  77. it('is a function', function () {
  78. expect(typeof Reflect.apply).to.equal('function');
  79. });
  80. ifFunctionsHaveNamesIt('has the right name', function () {
  81. expect(Reflect.apply.name).to.equal('apply');
  82. });
  83. it('throws if target isn’t callable', function () {
  84. testCallableThrow(function (item) {
  85. return Reflect.apply(item, null, []);
  86. });
  87. });
  88. it('works also with redefined apply', function () {
  89. expect(Reflect.apply(Array.prototype.push, [1, 2], [3, 4, 5])).to.equal(5);
  90. var F = function F(a, b, c) {
  91. return a + b + c;
  92. };
  93. F.apply = false;
  94. expect(Reflect.apply(F, null, [1, 2, 3])).to.equal(6);
  95. var G = function G(last) {
  96. return this.x + 'lo' + last;
  97. };
  98. G.apply = function nop() {};
  99. expect(Reflect.apply(G, { x: 'yel' }, ['!'])).to.equal('yello!');
  100. });
  101. });
  102. describe('.construct()', function () {
  103. if (typeof Reflect.construct === 'undefined') {
  104. return it('exists', function () {
  105. expect(Reflect).to.have.property('construct');
  106. });
  107. }
  108. it('is a function', function () {
  109. expect(typeof Reflect.construct).to.equal('function');
  110. });
  111. ifFunctionsHaveNamesIt('has the right name', function () {
  112. expect(Reflect.construct.name).to.equal('construct');
  113. });
  114. it('throws if target isn’t callable', function () {
  115. testCallableThrow(function (item) {
  116. return Reflect.apply(item, null, []);
  117. });
  118. });
  119. it('works also with redefined apply', function () {
  120. var C = function C(a, b, c) {
  121. this.qux = [a, b, c].join('|');
  122. };
  123. C.apply = undefined;
  124. expect(Reflect.construct(C, ['foo', 'bar', 'baz']).qux).to.equal('foo|bar|baz');
  125. });
  126. it('correctly handles newTarget param', function () {
  127. var F = function F() {};
  128. expect(Reflect.construct(function () {}, [], F) instanceof F).to.equal(true);
  129. });
  130. });
  131. describeIfES5('.defineProperty()', function () {
  132. if (typeof Reflect.defineProperty === 'undefined') {
  133. return it('exists', function () {
  134. expect(Reflect).to.have.property('defineProperty');
  135. });
  136. }
  137. it('is a function', function () {
  138. expect(typeof Reflect.defineProperty).to.equal('function');
  139. });
  140. ifFunctionsHaveNamesIt('has the right name', function () {
  141. expect(Reflect.defineProperty.name).to.equal('defineProperty');
  142. });
  143. it('throws if the target isn’t an object', function () {
  144. testPrimitiveThrow(function (item) {
  145. return Reflect.defineProperty(item, 'prop', { value: true });
  146. });
  147. });
  148. ifExtensionsPreventibleIt('returns false for non-extensible objects', function () {
  149. var o = Object.preventExtensions({});
  150. expect(Reflect.defineProperty(o, 'prop', {})).to.equal(false);
  151. });
  152. it('can return true, even for non-configurable, non-writable properties', function () {
  153. var o = {};
  154. var desc = {
  155. value: 13,
  156. enumerable: false,
  157. writable: false,
  158. configurable: false
  159. };
  160. expect(Reflect.defineProperty(o, 'prop', desc)).to.equal(true);
  161. // Defined as non-configurable, but descriptor is identical.
  162. expect(Reflect.defineProperty(o, 'prop', desc)).to.equal(true);
  163. desc.value = 37; // Change
  164. expect(Reflect.defineProperty(o, 'prop', desc)).to.equal(false);
  165. });
  166. it('can change from one property type to another, if configurable', function () {
  167. var o = {};
  168. var desc1 = {
  169. set: function () {},
  170. configurable: true
  171. };
  172. var desc2 = {
  173. value: 13,
  174. configurable: false
  175. };
  176. var desc3 = {
  177. get: function () {}
  178. };
  179. expect(Reflect.defineProperty(o, 'prop', desc1)).to.equal(true);
  180. expect(Reflect.defineProperty(o, 'prop', desc2)).to.equal(true);
  181. expect(Reflect.defineProperty(o, 'prop', desc3)).to.equal(false);
  182. });
  183. });
  184. describe('.deleteProperty()', function () {
  185. if (typeof Reflect.deleteProperty === 'undefined') {
  186. return it('exists', function () {
  187. expect(Reflect).to.have.property('deleteProperty');
  188. });
  189. }
  190. it('is a function', function () {
  191. expect(typeof Reflect.deleteProperty).to.equal('function');
  192. });
  193. ifFunctionsHaveNamesIt('has the right name', function () {
  194. expect(Reflect.deleteProperty.name).to.equal('deleteProperty');
  195. });
  196. it('throws if the target isn’t an object', function () {
  197. testPrimitiveThrow(function (item) {
  198. return Reflect.deleteProperty(item, 'prop');
  199. });
  200. });
  201. ifES5It('returns true for success and false for failure', function () {
  202. var o = { a: 1 };
  203. Object.defineProperty(o, 'b', { value: 2 });
  204. expect(o).to.have.property('a');
  205. expect(o).to.have.property('b');
  206. expect(o.a).to.equal(1);
  207. expect(o.b).to.equal(2);
  208. expect(Reflect.deleteProperty(o, 'a')).to.equal(true);
  209. expect(o).not.to.have.property('a');
  210. expect(o.b).to.equal(2);
  211. expect(Reflect.deleteProperty(o, 'b')).to.equal(false);
  212. expect(o).to.have.property('b');
  213. expect(o.b).to.equal(2);
  214. expect(Reflect.deleteProperty(o, 'a')).to.equal(true);
  215. });
  216. it('cannot delete an array’s length property', function () {
  217. expect(Reflect.deleteProperty([], 'length')).to.equal(false);
  218. });
  219. });
  220. describeIfES5('.get()', function () {
  221. if (typeof Reflect.get === 'undefined') {
  222. return it('exists', function () {
  223. expect(Reflect).to.have.property('get');
  224. });
  225. }
  226. it('is a function', function () {
  227. expect(typeof Reflect.get).to.equal('function');
  228. });
  229. ifFunctionsHaveNamesIt('has the right name', function () {
  230. expect(Reflect.get.name).to.equal('get');
  231. });
  232. it('throws on null and undefined', function () {
  233. [null, undefined].forEach(function (item) {
  234. expect(function () {
  235. return Reflect.get(item, 'property');
  236. }).to['throw'](TypeError);
  237. });
  238. });
  239. it('can retrieve a simple value, from the target', function () {
  240. var p = { something: 2, bool: false };
  241. expect(Reflect.get(object, 'something')).to.equal(1);
  242. // p has no effect
  243. expect(Reflect.get(object, 'something', p)).to.equal(1);
  244. // Value-defined properties take the target's value,
  245. // and ignore that of the receiver.
  246. expect(Reflect.get(object, 'bool', p)).to.equal(true);
  247. // Undefined values
  248. expect(Reflect.get(object, 'undefined_property')).to.equal(undefined);
  249. });
  250. it('will invoke getters on the receiver rather than target', function () {
  251. var other = { _value: 1337 };
  252. expect(Reflect.get(object, 'value', other)).to.equal(1337);
  253. // No getter for setter property
  254. expect(Reflect.get(object, 'setter', other)).to.equal(undefined);
  255. });
  256. it('will search the prototype chain', function () {
  257. var other = Object.create(object);
  258. other._value = 17;
  259. var yetAnother = { _value: 4711 };
  260. expect(Reflect.get(other, 'value', yetAnother)).to.equal(4711);
  261. expect(Reflect.get(other, 'bool', yetAnother)).to.equal(true);
  262. });
  263. });
  264. describeIfES5('.set()', function () {
  265. if (typeof Reflect.set === 'undefined') {
  266. return it('exists', function () {
  267. expect(Reflect).to.have.property('set');
  268. });
  269. }
  270. it('is a function', function () {
  271. expect(typeof Reflect.set).to.equal('function');
  272. });
  273. ifFunctionsHaveNamesIt('has the right name', function () {
  274. expect(Reflect.set.name).to.equal('set');
  275. });
  276. it('throws if the target isn’t an object', function () {
  277. testPrimitiveThrow(function (item) {
  278. return Reflect.set(item, 'prop', 'value');
  279. });
  280. });
  281. it('sets values on receiver', function () {
  282. var target = {};
  283. var receiver = {};
  284. expect(Reflect.set(target, 'foo', 1, receiver)).to.equal(true);
  285. expect('foo' in target).to.equal(false);
  286. expect(receiver.foo).to.equal(1);
  287. expect(Reflect.defineProperty(receiver, 'bar', {
  288. value: 0,
  289. writable: true,
  290. enumerable: false,
  291. configurable: true
  292. })).to.equal(true);
  293. expect(Reflect.set(target, 'bar', 1, receiver)).to.equal(true);
  294. expect(receiver.bar).to.equal(1);
  295. expect(Reflect.getOwnPropertyDescriptor(receiver, 'bar').enumerable).to.equal(false);
  296. var out;
  297. /* eslint-disable accessor-pairs */
  298. target = Object.create({}, {
  299. o: {
  300. set: function () { out = this; }
  301. }
  302. });
  303. /* eslint-enable accessor-pairs */
  304. expect(Reflect.set(target, 'o', 17, receiver)).to.equal(true);
  305. expect(out).to.equal(receiver);
  306. });
  307. });
  308. describeIfES5('.getOwnPropertyDescriptor()', function () {
  309. if (typeof Reflect.getOwnPropertyDescriptor === 'undefined') {
  310. return it('exists', function () {
  311. expect(Reflect).to.have.property('getOwnPropertyDescriptor');
  312. });
  313. }
  314. it('is a function', function () {
  315. expect(typeof Reflect.getOwnPropertyDescriptor).to.equal('function');
  316. });
  317. ifFunctionsHaveNamesIt('has the right name', function () {
  318. expect(Reflect.getOwnPropertyDescriptor.name).to.equal('getOwnPropertyDescriptor');
  319. });
  320. it('throws if the target isn’t an object', function () {
  321. testPrimitiveThrow(function (item) {
  322. return Reflect.getOwnPropertyDescriptor(item, 'prop');
  323. });
  324. });
  325. it('retrieves property descriptors', function () {
  326. var obj = { a: 4711 };
  327. var desc = Reflect.getOwnPropertyDescriptor(obj, 'a');
  328. expect(desc).to.deep.equal({
  329. value: 4711,
  330. configurable: true,
  331. writable: true,
  332. enumerable: true
  333. });
  334. });
  335. });
  336. describeIfGetProto('.getPrototypeOf()', function () {
  337. if (typeof Reflect.getPrototypeOf === 'undefined') {
  338. return it('exists', function () {
  339. expect(Reflect).to.have.property('getPrototypeOf');
  340. });
  341. }
  342. it('is a function', function () {
  343. expect(typeof Reflect.getPrototypeOf).to.equal('function');
  344. });
  345. ifFunctionsHaveNamesIt('has the right name', function () {
  346. expect(Reflect.getPrototypeOf.name).to.equal('getPrototypeOf');
  347. });
  348. it('throws if the target isn’t an object', function () {
  349. testPrimitiveThrow(function (item) {
  350. return Reflect.getPrototypeOf(item);
  351. });
  352. });
  353. it('retrieves prototypes', function () {
  354. expect(Reflect.getPrototypeOf(Object.create(null))).to.equal(null);
  355. expect(Reflect.getPrototypeOf([])).to.equal(Array.prototype);
  356. });
  357. });
  358. describe('.has()', function () {
  359. if (typeof Reflect.has === 'undefined') {
  360. return it('exists', function () {
  361. expect(Reflect).to.have.property('has');
  362. });
  363. }
  364. it('is a function', function () {
  365. expect(typeof Reflect.has).to.equal('function');
  366. });
  367. ifFunctionsHaveNamesIt('has the right name', function () {
  368. expect(Reflect.has.name).to.equal('has');
  369. });
  370. it('throws if the target isn’t an object', function () {
  371. testPrimitiveThrow(function (item) {
  372. return Reflect.has(item, 'prop');
  373. });
  374. });
  375. it('will detect own properties', function () {
  376. var target = Object.create ? Object.create(null) : {};
  377. expect(Reflect.has(target, 'prop')).to.equal(false);
  378. target.prop = undefined;
  379. expect(Reflect.has(target, 'prop')).to.equal(true);
  380. delete target.prop;
  381. expect(Reflect.has(target, 'prop')).to.equal(false);
  382. expect(Reflect.has(Reflect.has, 'length')).to.equal(true);
  383. });
  384. ifES5It('will detect an own accessor property', function () {
  385. var target = Object.create(null);
  386. /* eslint-disable accessor-pairs */
  387. Object.defineProperty(target, 'accessor', {
  388. set: function () {}
  389. });
  390. /* eslint-enable accessor-pairs */
  391. expect(Reflect.has(target, 'accessor')).to.equal(true);
  392. });
  393. it('will search the prototype chain', function () {
  394. var Parent = function () {};
  395. Parent.prototype.someProperty = undefined;
  396. var Child = function () {};
  397. Child.prototype = new Parent();
  398. var target = new Child();
  399. target.bool = true;
  400. expect(Reflect.has(target, 'bool')).to.equal(true);
  401. expect(Reflect.has(target, 'someProperty')).to.equal(true);
  402. expect(Reflect.has(target, 'undefinedProperty')).to.equal(false);
  403. });
  404. });
  405. describeIfExtensionsPreventible('.isExtensible()', function () {
  406. if (typeof Reflect.isExtensible === 'undefined') {
  407. return it('exists', function () {
  408. expect(Reflect).to.have.property('isExtensible');
  409. });
  410. }
  411. it('is a function', function () {
  412. expect(typeof Reflect.isExtensible).to.equal('function');
  413. });
  414. ifFunctionsHaveNamesIt('has the right name', function () {
  415. expect(Reflect.isExtensible.name).to.equal('isExtensible');
  416. });
  417. it('returns true for plain objects', function () {
  418. expect(Reflect.isExtensible({})).to.equal(true);
  419. expect(Reflect.isExtensible(Object.preventExtensions({}))).to.equal(false);
  420. });
  421. it('throws if the target isn’t an object', function () {
  422. testPrimitiveThrow(function (item) {
  423. return Reflect.isExtensible(item);
  424. });
  425. });
  426. });
  427. describeIfGetOwnPropertyNames('.ownKeys()', function () {
  428. if (typeof Reflect.ownKeys === 'undefined') {
  429. return it('exists', function () {
  430. expect(Reflect).to.have.property('ownKeys');
  431. });
  432. }
  433. it('is a function', function () {
  434. expect(typeof Reflect.ownKeys).to.equal('function');
  435. });
  436. ifFunctionsHaveNamesIt('has the right name', function () {
  437. expect(Reflect.ownKeys.name).to.equal('ownKeys');
  438. });
  439. it('throws if the target isn’t an object', function () {
  440. testPrimitiveThrow(function (item) {
  441. return Reflect.ownKeys(item);
  442. });
  443. });
  444. it('should return the same result as Object.getOwnPropertyNames if there are no Symbols', function () {
  445. var obj = { foo: 1, bar: 2 };
  446. obj[1] = 'first';
  447. var result = Object.getOwnPropertyNames(obj);
  448. // Reflect.ownKeys depends on the implementation of
  449. // Object.getOwnPropertyNames, at least for non-symbol keys.
  450. expect(Reflect.ownKeys(obj)).to.deep.equal(result);
  451. // We can only be sure of which keys should exist.
  452. expect(result.sort()).to.deep.equal(['1', 'bar', 'foo']);
  453. });
  454. ifSymbolsIt('symbols come last', function () {
  455. var s = Symbol();
  456. var o = {
  457. 'non-symbol': true
  458. };
  459. o[s] = true;
  460. expect(Reflect.ownKeys(o)).to.deep.equal(['non-symbol', s]);
  461. });
  462. });
  463. describeIfExtensionsPreventible('.preventExtensions()', function () {
  464. if (typeof Reflect.preventExtensions === 'undefined') {
  465. return it('exists', function () {
  466. expect(Reflect).to.have.property('preventExtensions');
  467. });
  468. }
  469. it('is a function', function () {
  470. expect(typeof Reflect.preventExtensions).to.equal('function');
  471. });
  472. ifFunctionsHaveNamesIt('has the right name', function () {
  473. expect(Reflect.preventExtensions.name).to.equal('preventExtensions');
  474. });
  475. it('throws if the target isn’t an object', function () {
  476. testPrimitiveThrow(function (item) {
  477. return Reflect.preventExtensions(item);
  478. });
  479. });
  480. it('prevents extensions on objects', function () {
  481. var obj = {};
  482. Reflect.preventExtensions(obj);
  483. expect(Object.isExtensible(obj)).to.equal(false);
  484. });
  485. });
  486. describeIfSetProto('.setPrototypeOf()', function () {
  487. if (typeof Reflect.setPrototypeOf === 'undefined') {
  488. return it('exists', function () {
  489. expect(Reflect).to.have.property('setPrototypeOf');
  490. });
  491. }
  492. it('is a function', function () {
  493. expect(typeof Reflect.setPrototypeOf).to.equal('function');
  494. });
  495. ifFunctionsHaveNamesIt('has the right name', function () {
  496. expect(Reflect.setPrototypeOf.name).to.equal('setPrototypeOf');
  497. });
  498. it('throws if the target isn’t an object', function () {
  499. testPrimitiveThrow(function (item) {
  500. return Reflect.setPrototypeOf(item, null);
  501. });
  502. });
  503. it('throws if the prototype is neither object nor null', function () {
  504. var o = {};
  505. [undefined, 1, 'string', true].forEach(function (item) {
  506. expect(function () {
  507. return Reflect.setPrototypeOf(o, item);
  508. }).to['throw'](TypeError);
  509. });
  510. });
  511. it('can set prototypes, and returns true on success', function () {
  512. var obj = {};
  513. expect(Reflect.setPrototypeOf(obj, Array.prototype)).to.equal(true);
  514. expect(obj).to.be.an.instanceOf(Array);
  515. expect(obj.toString).not.to.equal(undefined);
  516. expect(Reflect.setPrototypeOf(obj, null)).to.equal(true);
  517. expect(obj.toString).to.equal(undefined);
  518. });
  519. ifFreezeIt('is returns false on failure', function () {
  520. var obj = Object.freeze({});
  521. expect(Reflect.setPrototypeOf(obj, null)).to.equal(false);
  522. });
  523. it('fails when attempting to create a circular prototype chain', function () {
  524. var o = {};
  525. expect(Reflect.setPrototypeOf(o, o)).to.equal(false);
  526. });
  527. });
  528. });