8
0

operationreplayer.js 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  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 OperationReplayer from '../../src/dev-utils/operationreplayer';
  6. import Model from '../../src/model/model';
  7. describe( 'OperationReplayer', () => {
  8. describe( 'constructor()', () => {
  9. it( 'should be able to initialize replayer without operations', () => {
  10. const model = getModel();
  11. const stringifiedOperations = '';
  12. const operationReplayer = new OperationReplayer( model, '---', stringifiedOperations );
  13. expect( operationReplayer.getOperationsToReplay() ).to.deep.equal( [] );
  14. } );
  15. it( 'should be able to initialize replayer with operations', () => {
  16. const model = getModel();
  17. const operation = getFirstOperation();
  18. const operationReplayer = new OperationReplayer( model, '---', JSON.stringify( operation ) );
  19. expect( operationReplayer.getOperationsToReplay() ).to.deep.equal( [ operation ] );
  20. } );
  21. } );
  22. describe( 'applyNextOperation()', () => {
  23. it( 'should remove first operation from stack', () => {
  24. const model = getModel();
  25. const operation = getFirstOperation();
  26. const operationReplayer = new OperationReplayer( model, '---', JSON.stringify( operation ) );
  27. return operationReplayer.applyNextOperation().then( isFinished => {
  28. expect( operationReplayer.getOperationsToReplay() ).to.deep.equal( [] );
  29. expect( isFinished ).to.equal( false );
  30. } );
  31. } );
  32. it( 'should apply first operation on the document', () => {
  33. const model = getModel();
  34. const operation = getFirstOperation();
  35. const operationReplayer = new OperationReplayer( model, '---', JSON.stringify( operation ) );
  36. return operationReplayer.applyNextOperation().then( () => {
  37. expect( Array.from( model.document.getRoot().getChildren() ).length ).to.equal( 1 );
  38. } );
  39. } );
  40. it( 'should resolve with true if 0 operations are provided', () => {
  41. const model = getModel();
  42. const operationReplayer = new OperationReplayer( model, '---', '' );
  43. return operationReplayer.applyNextOperation().then( isFinished => {
  44. expect( isFinished ).to.equal( true );
  45. } );
  46. } );
  47. } );
  48. describe( 'applyAllOperations()', () => {
  49. it( 'should apply all operations on the document', () => {
  50. const model = getModel();
  51. const stringifiedOperations = [ getFirstOperation(), getSecondOperation() ]
  52. .map( d => JSON.stringify( d ) )
  53. .join( '---' );
  54. const operationReplayer = new OperationReplayer( model, '---', stringifiedOperations );
  55. return operationReplayer.applyAllOperations().then( () => {
  56. expect( Array.from( model.document.getRoot().getChildren() ).length ).to.equal( 2 );
  57. expect( operationReplayer.getOperationsToReplay().length ).to.equal( 0 );
  58. } );
  59. } );
  60. } );
  61. describe( 'applyOperations()', () => {
  62. it( 'should apply certain number of operations on the document', () => {
  63. const model = getModel();
  64. const stringifiedOperations = [ getFirstOperation(), getSecondOperation() ]
  65. .map( d => JSON.stringify( d ) )
  66. .join( '---' );
  67. const operationReplayer = new OperationReplayer( model, '---', stringifiedOperations );
  68. return operationReplayer.applyOperations( 1 ).then( () => {
  69. expect( Array.from( model.document.getRoot().getChildren() ).length ).to.equal( 1 );
  70. expect( operationReplayer.getOperationsToReplay().length ).to.equal( 1 );
  71. } );
  72. } );
  73. it( 'should not throw an error if the number of operations is lower than number of expected operations to replay', () => {
  74. const model = getModel();
  75. const stringifiedOperations = [ getFirstOperation(), getSecondOperation() ]
  76. .map( d => JSON.stringify( d ) )
  77. .join( '---' );
  78. const operationReplayer = new OperationReplayer( model, '---', stringifiedOperations );
  79. return operationReplayer.applyOperations( 3 ).then( () => {
  80. expect( Array.from( model.document.getRoot().getChildren() ).length ).to.equal( 2 );
  81. expect( operationReplayer.getOperationsToReplay().length ).to.equal( 0 );
  82. } );
  83. } );
  84. } );
  85. describe( 'play()', () => {
  86. it( 'should play operations with time interval', () => {
  87. const model = getModel();
  88. const stringifiedOperations = [ getFirstOperation(), getSecondOperation() ]
  89. .map( d => JSON.stringify( d ) )
  90. .join( '---' );
  91. const operationReplayer = new OperationReplayer( model, '---', stringifiedOperations );
  92. return operationReplayer.play( 0 ).then( () => {
  93. expect( operationReplayer.getOperationsToReplay().length ).to.equal( 0 );
  94. } );
  95. } );
  96. it( 'should work with default time interval', () => {
  97. const model = getModel();
  98. const operationReplayer = new OperationReplayer( model, '---', '' );
  99. return operationReplayer.play();
  100. } );
  101. it( 'should correctly handle errors coming from the engine', () => {
  102. const model = getModel();
  103. const invalidOperation = getSecondOperation();
  104. invalidOperation.baseVersion = 3;
  105. const stringifiedOperations = [ getFirstOperation(), invalidOperation ]
  106. .map( d => JSON.stringify( d ) )
  107. .join( '---' );
  108. const operationReplayer = new OperationReplayer( model, '---', stringifiedOperations );
  109. return operationReplayer.play( 1 )
  110. .then( () => {
  111. throw new Error( 'It should throw an error' );
  112. }, err => {
  113. expect( err.message ).to.match( /model-document-applyoperation-wrong-version/ );
  114. } );
  115. } );
  116. } );
  117. } );
  118. function getModel() {
  119. const model = new Model();
  120. model.document.createRoot();
  121. return model;
  122. }
  123. function getFirstOperation() {
  124. return {
  125. baseVersion: 0,
  126. position: {
  127. root: 'main',
  128. path: [ 0 ]
  129. },
  130. nodes: [ {
  131. name: 'heading1',
  132. children: [ {
  133. data: 'The great world of open Web standards'
  134. } ]
  135. } ],
  136. __className: 'InsertOperation'
  137. };
  138. }
  139. function getSecondOperation() {
  140. return {
  141. baseVersion: 1,
  142. position: {
  143. root: 'main',
  144. path: [ 1 ]
  145. },
  146. nodes: [ {
  147. name: 'heading2',
  148. children: [ {
  149. data: 'The great world of open Web standards'
  150. } ]
  151. } ],
  152. __className: 'InsertOperation'
  153. };
  154. }