utils.js 7.6 KB

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