mix.js 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. /**
  2. * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
  3. * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  4. */
  5. import mix from '../src/mix';
  6. describe( 'utils', () => {
  7. describe( 'mix', () => {
  8. const MixinA = {
  9. a() {
  10. return 'a';
  11. }
  12. };
  13. const MixinB = {
  14. b() {
  15. return 'b';
  16. }
  17. };
  18. it( 'mixes 2nd+ argument\'s properties into the first class', () => {
  19. class Foo {}
  20. mix( Foo, MixinA, MixinB );
  21. expect( Foo ).to.not.have.property( 'a' );
  22. expect( Foo ).to.not.have.property( 'b' );
  23. const foo = new Foo();
  24. expect( foo.a() ).to.equal( 'a' );
  25. expect( foo.b() ).to.equal( 'b' );
  26. } );
  27. it( 'does not break the instanceof operator', () => {
  28. class Foo {}
  29. mix( Foo, MixinA );
  30. const foo = new Foo();
  31. expect( foo ).to.be.instanceof( Foo );
  32. } );
  33. it( 'defines properties with the same descriptors as native classes', () => {
  34. class Foo {
  35. foo() {}
  36. }
  37. mix( Foo, MixinA );
  38. const actualDescriptor = Object.getOwnPropertyDescriptor( Foo.prototype, 'a' );
  39. const expectedDescriptor = Object.getOwnPropertyDescriptor( Foo.prototype, 'foo' );
  40. expect( actualDescriptor ).to.have.property( 'writable', expectedDescriptor.writable );
  41. expect( actualDescriptor ).to.have.property( 'enumerable', expectedDescriptor.enumerable );
  42. expect( actualDescriptor ).to.have.property( 'configurable', expectedDescriptor.configurable );
  43. } );
  44. it( 'copies setters and getters (with descriptors as of native classes)', () => {
  45. class Foo {
  46. set foo( v ) {
  47. this._foo = v;
  48. }
  49. get foo() { return 'whatever'; }
  50. }
  51. const Mixin = {
  52. set a( v ) {
  53. this._a = v;
  54. },
  55. get a() {
  56. return this._a;
  57. }
  58. };
  59. mix( Foo, Mixin );
  60. const actualDescriptor = Object.getOwnPropertyDescriptor( Foo.prototype, 'a' );
  61. const expectedDescriptor = Object.getOwnPropertyDescriptor( Foo.prototype, 'foo' );
  62. expect( actualDescriptor ).to.have.property( 'enumerable', expectedDescriptor.enumerable );
  63. expect( actualDescriptor ).to.have.property( 'configurable', expectedDescriptor.configurable );
  64. const foo = new Foo();
  65. foo.a = 1;
  66. expect( foo._a ).to.equal( 1 );
  67. foo._a = 2;
  68. expect( foo.a ).to.equal( 2 );
  69. } );
  70. it( 'copies symbols (with descriptors as of native classes)', () => {
  71. const symbolA = Symbol( 'a' );
  72. const symbolFoo = Symbol( 'foo' );
  73. class Foo {
  74. [ symbolFoo ]() {
  75. return 'foo';
  76. }
  77. }
  78. const Mixin = {
  79. [ symbolA ]() {
  80. return 'a';
  81. }
  82. };
  83. // jscs:enable disallowSpacesInFunction
  84. mix( Foo, Mixin );
  85. const actualDescriptor = Object.getOwnPropertyDescriptor( Foo.prototype, symbolA );
  86. const expectedDescriptor = Object.getOwnPropertyDescriptor( Foo.prototype, symbolFoo );
  87. expect( actualDescriptor ).to.have.property( 'writable', expectedDescriptor.writable );
  88. expect( actualDescriptor ).to.have.property( 'enumerable', expectedDescriptor.enumerable );
  89. expect( actualDescriptor ).to.have.property( 'configurable', expectedDescriptor.configurable );
  90. const foo = new Foo();
  91. expect( foo[ symbolA ]() ).to.equal( 'a' );
  92. } );
  93. it( 'does not copy already existing properties', () => {
  94. class Foo {
  95. a() {
  96. return 'foo';
  97. }
  98. }
  99. mix( Foo, MixinA, MixinB );
  100. const foo = new Foo();
  101. expect( foo.a() ).to.equal( 'foo' );
  102. expect( foo.b() ).to.equal( 'b' );
  103. } );
  104. it( 'does not copy already existing properties - properties deep in the proto chain', () => {
  105. class Foo {
  106. a() {
  107. return 'foo';
  108. }
  109. }
  110. class Bar extends Foo {}
  111. mix( Bar, MixinA, MixinB );
  112. const bar = new Bar();
  113. expect( bar.a() ).to.equal( 'foo' );
  114. expect( bar.b() ).to.equal( 'b' );
  115. } );
  116. } );
  117. } );