utils.js 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315
  1. /**
  2. * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
  3. * For licensing, see LICENSE.md.
  4. */
  5. 'use strict';
  6. import utils from '/ckeditor5/core/utils.js';
  7. import coreTestUtils from '/tests/core/_utils/utils.js';
  8. const getIteratorCount = coreTestUtils.getIteratorCount;
  9. describe( 'utils', () => {
  10. describe( 'spy', () => {
  11. it( 'should not have `called` after creation', () => {
  12. let spy = utils.spy();
  13. expect( spy.called ).to.not.be.true();
  14. } );
  15. it( 'should register calls', () => {
  16. let fn1 = utils.spy();
  17. let fn2 = utils.spy();
  18. fn1();
  19. expect( fn1.called ).to.be.true();
  20. expect( fn2.called ).to.not.be.true();
  21. } );
  22. } );
  23. describe( 'uid', () => {
  24. it( 'should return different ids', () => {
  25. let id1 = utils.uid();
  26. let id2 = utils.uid();
  27. let id3 = utils.uid();
  28. expect( id1 ).to.be.a( 'number' );
  29. expect( id2 ).to.be.a( 'number' ).to.not.equal( id1 ).to.not.equal( id3 );
  30. expect( id3 ).to.be.a( 'number' ).to.not.equal( id1 ).to.not.equal( id2 );
  31. } );
  32. } );
  33. describe( 'isIterable', () => {
  34. it( 'should be true for string', () => {
  35. let string = 'foo';
  36. expect( utils.isIterable( string ) ).to.be.true;
  37. } );
  38. it( 'should be true for arrays', () => {
  39. let array = [ 1, 2, 3 ];
  40. expect( utils.isIterable( array ) ).to.be.true;
  41. } );
  42. it( 'should be true for iterable classes', () => {
  43. class IterableClass {
  44. constructor() {
  45. this.array = [ 1, 2, 3 ];
  46. }
  47. [ Symbol.iterator ]() {
  48. return this.array[ Symbol.iterator ]();
  49. }
  50. }
  51. let instance = new IterableClass();
  52. expect( utils.isIterable( instance ) ).to.be.true;
  53. } );
  54. it( 'should be false for not iterable objects', () => {
  55. let notIterable = { foo: 'bar' };
  56. expect( utils.isIterable( notIterable ) ).to.be.false;
  57. } );
  58. it( 'should be false for undefined', () => {
  59. expect( utils.isIterable() ).to.be.false;
  60. } );
  61. } );
  62. describe( 'compareArrays', () => {
  63. it( 'should return SAME flag, when arrays are same', () => {
  64. let a = [ 'abc', 0, 3 ];
  65. let b = [ 'abc', 0, 3 ];
  66. let result = utils.compareArrays( a, b );
  67. expect( result ).to.equal( 'SAME' );
  68. } );
  69. it( 'should return PREFIX flag, when all n elements of first array are same as n first elements of the second array', () => {
  70. let a = [ 'abc', 0 ];
  71. let b = [ 'abc', 0, 3 ];
  72. let result = utils.compareArrays( a, b );
  73. expect( result ).to.equal( 'PREFIX' );
  74. } );
  75. it( 'should return EXTENSION flag, when n first elements of first array are same as all elements of the second array', () => {
  76. let a = [ 'abc', 0, 3 ];
  77. let b = [ 'abc', 0 ];
  78. let result = utils.compareArrays( a, b );
  79. expect( result ).to.equal( 'EXTENSION' );
  80. } );
  81. it( 'should return index on which arrays differ, when arrays are not the same', () => {
  82. let a = [ 'abc', 0, 3 ];
  83. let b = [ 'abc', 1, 3 ];
  84. let result = utils.compareArrays( a, b );
  85. expect( result ).to.equal( 1 );
  86. } );
  87. } );
  88. describe( 'objectToMap', () => {
  89. it( 'should convert object to map', () => {
  90. const map = utils.objectToMap( { 'foo': 1, 'bar': 2 } );
  91. expect( getIteratorCount( map ) ).to.equal( 2 );
  92. expect( map.get( 'foo' ) ).to.equal( 1 );
  93. expect( map.get( 'bar' ) ).to.equal( 2 );
  94. } );
  95. } );
  96. describe( 'mapToObject', () => {
  97. it( 'should convert map to object', () => {
  98. const map = new Map( [ [ 'foo', 1 ], [ 'bar', 2 ] ] );
  99. const obj = utils.mapToObject( map );
  100. expect( obj ).to.deep.equal( { 'foo': 1, 'bar': 2 } );
  101. } );
  102. } );
  103. describe( 'nth', () => {
  104. it( 'should return 0th item', () => {
  105. expect( utils.nth( 0, getIterator() ) ).to.equal( 11 );
  106. } );
  107. it( 'should return the last item', () => {
  108. expect( utils.nth( 2, getIterator() ) ).to.equal( 33 );
  109. } );
  110. it( 'should return null if out of range (bottom)', () => {
  111. expect( utils.nth( -1, getIterator() ) ).to.be.null;
  112. } );
  113. it( 'should return null if out of range (top)', () => {
  114. expect( utils.nth( 3, getIterator() ) ).to.be.null;
  115. } );
  116. it( 'should return null if iterator is empty', () => {
  117. expect( utils.nth( 0, [] ) ).to.be.null;
  118. } );
  119. function *getIterator() {
  120. yield 11;
  121. yield 22;
  122. yield 33;
  123. }
  124. } );
  125. describe( 'mix', () => {
  126. const MixinA = {
  127. a() {
  128. return 'a';
  129. }
  130. };
  131. const MixinB = {
  132. b() {
  133. return 'b';
  134. }
  135. };
  136. it( 'mixes 2nd+ argument\'s properties into the first class', () => {
  137. class Foo {}
  138. utils.mix( Foo, MixinA, MixinB );
  139. expect( Foo ).to.not.have.property( 'a' );
  140. expect( Foo ).to.not.have.property( 'b' );
  141. const foo = new Foo();
  142. expect( foo.a() ).to.equal( 'a' );
  143. expect( foo.b() ).to.equal( 'b' );
  144. } );
  145. it( 'does not break the instanceof operator', () => {
  146. class Foo {}
  147. utils.mix( Foo, MixinA );
  148. let foo = new Foo();
  149. expect( foo ).to.be.instanceof( Foo );
  150. } );
  151. it( 'defines properties with the same descriptors as native classes', () => {
  152. class Foo {
  153. foo() {}
  154. }
  155. utils.mix( Foo, MixinA );
  156. const actualDescriptor = Object.getOwnPropertyDescriptor( Foo.prototype, 'a' );
  157. const expectedDescriptor = Object.getOwnPropertyDescriptor( Foo.prototype, 'foo' );
  158. expect( actualDescriptor ).to.have.property( 'writable', expectedDescriptor.writable );
  159. expect( actualDescriptor ).to.have.property( 'enumerable', expectedDescriptor.enumerable );
  160. expect( actualDescriptor ).to.have.property( 'configurable', expectedDescriptor.configurable );
  161. } );
  162. it( 'copies setters and getters (with descriptors as of native classes)', () => {
  163. class Foo {
  164. set foo( v ) {
  165. this._foo = v;
  166. }
  167. get foo() {}
  168. }
  169. const Mixin = {
  170. set a( v ) {
  171. this._a = v;
  172. },
  173. get a() {
  174. return this._a;
  175. }
  176. };
  177. utils.mix( Foo, Mixin );
  178. const actualDescriptor = Object.getOwnPropertyDescriptor( Foo.prototype, 'a' );
  179. const expectedDescriptor = Object.getOwnPropertyDescriptor( Foo.prototype, 'foo' );
  180. expect( actualDescriptor ).to.have.property( 'enumerable', expectedDescriptor.enumerable );
  181. expect( actualDescriptor ).to.have.property( 'configurable', expectedDescriptor.configurable );
  182. const foo = new Foo();
  183. foo.a = 1;
  184. expect( foo._a ).to.equal( 1 );
  185. foo._a = 2;
  186. expect( foo.a ).to.equal( 2 );
  187. } );
  188. it( 'copies symbols (with descriptors as of native classes)', () => {
  189. const symbolA = Symbol( 'a' );
  190. const symbolFoo = Symbol( 'foo' );
  191. // https://github.com/jscs-dev/node-jscs/issues/2078
  192. // jscs:disable disallowSpacesInFunction
  193. class Foo {
  194. [ symbolFoo ]() {
  195. return 'foo';
  196. }
  197. }
  198. const Mixin = {
  199. [ symbolA ]() {
  200. return 'a';
  201. }
  202. };
  203. // jscs:enable disallowSpacesInFunction
  204. utils.mix( Foo, Mixin );
  205. const actualDescriptor = Object.getOwnPropertyDescriptor( Foo.prototype, symbolA );
  206. const expectedDescriptor = Object.getOwnPropertyDescriptor( Foo.prototype, symbolFoo );
  207. expect( actualDescriptor ).to.have.property( 'writable', expectedDescriptor.writable );
  208. expect( actualDescriptor ).to.have.property( 'enumerable', expectedDescriptor.enumerable );
  209. expect( actualDescriptor ).to.have.property( 'configurable', expectedDescriptor.configurable );
  210. const foo = new Foo();
  211. expect( foo[ symbolA ]() ).to.equal( 'a' );
  212. } );
  213. it( 'does not copy already existing properties', () => {
  214. class Foo {
  215. a() {
  216. return 'foo';
  217. }
  218. }
  219. utils.mix( Foo, MixinA, MixinB );
  220. const foo = new Foo();
  221. expect( foo.a() ).to.equal( 'foo' );
  222. expect( foo.b() ).to.equal( 'b' );
  223. } );
  224. it( 'does not copy already existing properties - properties deep in the proto chain', () => {
  225. class Foo {
  226. a() {
  227. return 'foo';
  228. }
  229. }
  230. class Bar extends Foo {}
  231. utils.mix( Bar, MixinA, MixinB );
  232. const bar = new Bar();
  233. expect( bar.a() ).to.equal( 'foo' );
  234. expect( bar.b() ).to.equal( 'b' );
  235. } );
  236. } );
  237. } );