edit_operation.test.js 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. const { expect } = require('chai')
  2. const EditOperationBuilder = require('../lib/operation/edit_operation_builder')
  3. const TextOperation = require('../lib/operation/text_operation')
  4. const EditOperationTransformer = require('../lib/operation/edit_operation_transformer')
  5. const EditOperation = require('../lib/operation/edit_operation')
  6. const randomTextOperation = require('./support/random_text_operation')
  7. const random = require('./support/random')
  8. const AddCommentOperation = require('../lib/operation/add_comment_operation')
  9. const DeleteCommentOperation = require('../lib/operation/delete_comment_operation')
  10. const Comment = require('../lib/comment')
  11. const Range = require('../lib/range')
  12. const EditNoOperation = require('../lib/operation/edit_no_operation')
  13. describe('EditOperation', function () {
  14. it('Cannot be instantiated', function () {
  15. expect(() => new EditOperation()).to.throw(
  16. 'Cannot instantiate abstract class'
  17. )
  18. })
  19. })
  20. describe('EditOperationTransformer', function () {
  21. it('Transforms two TextOperations', function () {
  22. const a = new TextOperation().insert('foo')
  23. const b = new TextOperation().insert('bar')
  24. const [aPrime, bPrime] = EditOperationTransformer.transform(a, b)
  25. expect(aPrime).to.be.an.instanceof(TextOperation)
  26. expect(bPrime).to.be.an.instanceof(TextOperation)
  27. })
  28. it('Transforms TextOperation and EditNoOperation', function () {
  29. const a = new TextOperation().insert('foo')
  30. const b = new EditNoOperation()
  31. const [aPrime, bPrime] = EditOperationTransformer.transform(a, b)
  32. expect(aPrime).to.be.an.instanceof(TextOperation)
  33. expect(bPrime).to.be.an.instanceof(EditNoOperation)
  34. })
  35. it('Transforms two AddCommentOperations with same commentId', function () {
  36. const a = new AddCommentOperation('comm1', new Comment([new Range(0, 1)]))
  37. const b = new AddCommentOperation('comm1', new Comment([new Range(2, 3)]))
  38. const [aPrime, bPrime] = EditOperationTransformer.transform(a, b)
  39. expect(aPrime).to.be.an.instanceof(AddCommentOperation)
  40. expect(bPrime).to.be.an.instanceof(AddCommentOperation)
  41. })
  42. it('Transforms two AddCommentOperations with different commentId', function () {
  43. const a = new AddCommentOperation('comm1', new Comment([new Range(0, 1)]))
  44. const b = new AddCommentOperation('comm2', new Comment([new Range(2, 3)]))
  45. const [aPrime, bPrime] = EditOperationTransformer.transform(a, b)
  46. expect(aPrime).to.be.an.instanceof(AddCommentOperation)
  47. expect(aPrime.toJSON()).to.eql(a.toJSON())
  48. expect(bPrime).to.be.an.instanceof(AddCommentOperation)
  49. expect(bPrime.toJSON()).to.eql(b.toJSON())
  50. })
  51. it('Transforms two DeleteCommentOperations with same commentId', function () {
  52. const a = new DeleteCommentOperation('comm1')
  53. const b = new DeleteCommentOperation('comm1')
  54. const [aPrime, bPrime] = EditOperationTransformer.transform(a, b)
  55. expect(aPrime).to.be.an.instanceof(EditNoOperation)
  56. expect(bPrime).to.be.an.instanceof(EditNoOperation)
  57. })
  58. it('Transforms two DeleteCommentOperations with different commentId', function () {
  59. const a = new DeleteCommentOperation('comm1')
  60. const b = new DeleteCommentOperation('comm2')
  61. const [aPrime, bPrime] = EditOperationTransformer.transform(a, b)
  62. expect(aPrime).to.be.an.instanceof(DeleteCommentOperation)
  63. expect(aPrime.toJSON()).to.eql(a.toJSON())
  64. expect(bPrime).to.be.an.instanceof(DeleteCommentOperation)
  65. expect(bPrime.toJSON()).to.eql(b.toJSON())
  66. })
  67. it('Transforms AddCommentOperation and DeleteCommentOperation with same commentId', function () {
  68. const a = new AddCommentOperation('comm1', new Comment([new Range(0, 1)]))
  69. const b = new DeleteCommentOperation('comm1')
  70. const [aPrime, bPrime] = EditOperationTransformer.transform(a, b)
  71. expect(aPrime).to.be.an.instanceof(EditNoOperation)
  72. expect(bPrime).to.be.an.instanceof(DeleteCommentOperation)
  73. expect(bPrime.toJSON()).to.eql(b.toJSON())
  74. })
  75. it('Transforms DeleteCommentOperation and AddCommentOperation with same commentId', function () {
  76. const a = new DeleteCommentOperation('comm1')
  77. const b = new AddCommentOperation('comm1', new Comment([new Range(0, 1)]))
  78. const [aPrime, bPrime] = EditOperationTransformer.transform(a, b)
  79. expect(aPrime).to.be.an.instanceof(DeleteCommentOperation)
  80. expect(aPrime.toJSON()).to.eql(a.toJSON())
  81. expect(bPrime).to.be.an.instanceof(EditNoOperation)
  82. })
  83. it('Transforms AddCommentOperation and TextOperation', function () {
  84. // abc hello[ world] xyz - insert(9, " world")
  85. // abc hello |xyz| - addComment(10, 3, "comment_id")
  86. const a = new TextOperation().retain(9).insert(' world')
  87. const b = new AddCommentOperation('comm1', new Comment([new Range(10, 3)]))
  88. const [aPrime, bPrime] = EditOperationTransformer.transform(a, b)
  89. expect(aPrime).to.be.an.instanceof(TextOperation)
  90. expect(aPrime.toJSON()).to.eql(a.toJSON())
  91. expect(bPrime).to.be.an.instanceof(AddCommentOperation)
  92. expect(bPrime.toJSON()).to.eql({
  93. commentId: 'comm1',
  94. ranges: [{ pos: 16, length: 3 }],
  95. resolved: false,
  96. })
  97. })
  98. it('Transforms TextOperation and AddCommentOperation', function () {
  99. // abc hello |xyz| - addComment(10, 3, "comment_id")
  100. // abc hello[ world] xyz - insert(9, " world")
  101. const a = new AddCommentOperation('comm1', new Comment([new Range(10, 3)]))
  102. const b = new TextOperation().retain(9).insert(' world')
  103. const [aPrime, bPrime] = EditOperationTransformer.transform(a, b)
  104. expect(bPrime).to.be.an.instanceof(TextOperation)
  105. expect(bPrime.toJSON()).to.eql(b.toJSON())
  106. expect(aPrime).to.be.an.instanceof(AddCommentOperation)
  107. expect(aPrime.toJSON()).to.eql({
  108. commentId: 'comm1',
  109. ranges: [{ pos: 16, length: 3 }],
  110. resolved: false,
  111. })
  112. })
  113. it('Transforms AddCommentOperation and TextOperation that makes a detached comment', function () {
  114. // [abc hello xyz] - delete(0, 13)
  115. // abc |hello| xyz - addComment(5, 5, "comment_id")
  116. const a = new TextOperation().remove(13)
  117. const b = new AddCommentOperation('comm1', new Comment([new Range(5, 5)]))
  118. const [aPrime, bPrime] = EditOperationTransformer.transform(a, b)
  119. expect(aPrime).to.be.an.instanceof(TextOperation)
  120. expect(aPrime.toJSON()).to.eql(a.toJSON())
  121. expect(bPrime).to.be.an.instanceof(AddCommentOperation)
  122. expect(bPrime.toJSON()).to.eql({
  123. commentId: 'comm1',
  124. ranges: [],
  125. resolved: false,
  126. })
  127. })
  128. it('Transforms AddCommentOperation and deletion TextOperation', function () {
  129. // abc hell{o xy}z - retain(8).delete(4)
  130. // abc hello |xyz| - addComment(10, 3, "comment_id")
  131. // abc hell|z|
  132. const a = new TextOperation().retain(8).remove(4)
  133. const b = new AddCommentOperation('comm1', new Comment([new Range(10, 3)]))
  134. const [aPrime, bPrime] = EditOperationTransformer.transform(a, b)
  135. expect(aPrime).to.be.an.instanceof(TextOperation)
  136. expect(aPrime.toJSON()).to.eql(a.toJSON())
  137. expect(bPrime).to.be.an.instanceof(AddCommentOperation)
  138. expect(bPrime.toJSON()).to.eql({
  139. commentId: 'comm1',
  140. ranges: [{ pos: 8, length: 1 }],
  141. resolved: false,
  142. })
  143. })
  144. it('Transforms AddCommentOperation and complex TextOperation', function () {
  145. // [foo ]abc hell{o xy}z - insert(0, "foo ").retain(8).delete(4)
  146. // abc hello |xyz| - addComment(10, 3, "comment_id")
  147. // foo abc hell|z|
  148. const a = new TextOperation().insert('foo ').retain(8).remove(4)
  149. const b = new AddCommentOperation('comm1', new Comment([new Range(10, 3)]))
  150. const [aPrime, bPrime] = EditOperationTransformer.transform(a, b)
  151. expect(aPrime).to.be.an.instanceof(TextOperation)
  152. expect(aPrime.toJSON()).to.eql(a.toJSON())
  153. expect(bPrime).to.be.an.instanceof(AddCommentOperation)
  154. expect(bPrime.toJSON()).to.eql({
  155. commentId: 'comm1',
  156. ranges: [{ pos: 12, length: 1 }],
  157. resolved: false,
  158. })
  159. })
  160. it('Transforms DeleteCommentOperation and TextOperation', function () {
  161. const a = new TextOperation().retain(9).insert(' world')
  162. const b = new DeleteCommentOperation('comm1')
  163. const [aPrime, bPrime] = EditOperationTransformer.transform(a, b)
  164. expect(aPrime).to.be.an.instanceof(TextOperation)
  165. expect(aPrime.toJSON()).to.eql(a.toJSON())
  166. expect(bPrime).to.be.an.instanceof(DeleteCommentOperation)
  167. expect(bPrime.toJSON()).to.eql(b.toJSON())
  168. })
  169. })
  170. describe('EditOperationBuilder', function () {
  171. it('Constructs TextOperation from JSON', function () {
  172. const raw = {
  173. textOperation: [1, 'foo', 3],
  174. }
  175. const op = EditOperationBuilder.fromJSON(raw)
  176. expect(op).to.be.an.instanceof(TextOperation)
  177. expect(op.toJSON()).to.deep.equal(raw)
  178. })
  179. it('Throws error for unsupported operation', function () {
  180. const raw = {
  181. unsupportedOperation: {
  182. op: 'foo',
  183. },
  184. }
  185. expect(() => EditOperationBuilder.fromJSON(raw)).to.throw(
  186. 'Unsupported operation in EditOperationBuilder.fromJSON'
  187. )
  188. })
  189. it('Constructs TextOperation from JSON (randomised)', function () {
  190. const str = random.string(50)
  191. const randomOperation = randomTextOperation(str)
  192. const op = EditOperationBuilder.fromJSON(randomOperation.toJSON())
  193. expect(op).to.be.an.instanceof(TextOperation)
  194. expect(op.equals(randomOperation)).to.be.true
  195. })
  196. })