line-tracker.test.ts 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  1. import { LineTracker } from '../../../../../../frontend/js/features/source-editor/extensions/spelling/line-tracker'
  2. // import sinon from 'sinon'
  3. import { expect } from 'chai'
  4. import { EditorView } from '@codemirror/view'
  5. import { EditorState } from '@codemirror/state'
  6. import { Word } from '../../../../../../frontend/js/features/source-editor/extensions/spelling/spellchecker'
  7. const doc = [
  8. 'Hello test one two',
  9. 'three four five six',
  10. 'seven eight nine.',
  11. ].join('\n')
  12. describe('LineTracker', function () {
  13. describe('basic operations', function () {
  14. let state: EditorState,
  15. view: EditorView,
  16. lineTracker: LineTracker,
  17. check: (spec: [number, any][]) => void
  18. beforeEach(function () {
  19. state = EditorState.create({
  20. doc,
  21. extensions: [
  22. EditorView.updateListener.of(update => {
  23. lineTracker.applyUpdate(update)
  24. }),
  25. ],
  26. })
  27. view = new EditorView({ state })
  28. lineTracker = new LineTracker(view.state.doc)
  29. check = spec => {
  30. spec.forEach(([n, expectedValue]) => {
  31. expect(lineTracker.lineHasChanged(n)).to.equal(expectedValue)
  32. })
  33. }
  34. })
  35. it('start with the correct number of lines', function () {
  36. expect(state.doc.lines).to.equal(3)
  37. expect(lineTracker.count()).to.equal(state.doc.lines)
  38. })
  39. it('starts with all lines marked as changed', function () {
  40. expect(state.doc.lines).to.equal(3)
  41. check([
  42. [1, true],
  43. [2, true],
  44. [3, true],
  45. ])
  46. })
  47. it('clears a line', function () {
  48. check([[1, true]])
  49. lineTracker.clearLine(1)
  50. check([[1, false]])
  51. })
  52. it('clears lines based on a list of words', function () {
  53. lineTracker.clearChangedLinesForWords([
  54. { lineNumber: 1 },
  55. { lineNumber: 3 },
  56. ] as Word[])
  57. check([
  58. [1, false],
  59. [2, true],
  60. [3, false],
  61. ])
  62. })
  63. it('should update lines in response to text insertion', function () {
  64. lineTracker.clearChangedLinesForWords([
  65. { lineNumber: 1 },
  66. { lineNumber: 2 },
  67. { lineNumber: 3 },
  68. ] as Word[])
  69. check([
  70. [1, false],
  71. [2, false],
  72. [3, false],
  73. ])
  74. let transaction = view.state.update({
  75. changes: [{ from: 0, insert: 'x' }],
  76. })
  77. view.dispatch(transaction)
  78. check([
  79. [1, true],
  80. [2, false],
  81. [3, false],
  82. ])
  83. transaction = view.state.update({
  84. changes: [{ from: view.state.doc.length - 2, insert: 'x' }],
  85. })
  86. view.dispatch(transaction)
  87. check([
  88. [1, true],
  89. [2, false],
  90. [3, true],
  91. ])
  92. })
  93. it('should update lines in response to large text insertion', function () {
  94. lineTracker.clearChangedLinesForWords([
  95. { lineNumber: 1 },
  96. { lineNumber: 2 },
  97. { lineNumber: 3 },
  98. ] as Word[])
  99. check([
  100. [1, false],
  101. [2, false],
  102. [3, false],
  103. ])
  104. const text = new Array(1000).fill('x').join('\n')
  105. const transaction = view.state.update({
  106. changes: [{ from: 0, insert: text }],
  107. })
  108. view.dispatch(transaction)
  109. expect(lineTracker.count()).to.equal(1002)
  110. const expectations: [number, boolean][] = []
  111. for (let i = 1; i <= 1000; i++) {
  112. expectations.push([i, true])
  113. }
  114. expectations.push([1001, false])
  115. expectations.push([1002, false])
  116. check(expectations)
  117. })
  118. it('should update lines in response to removal of a line', function () {
  119. lineTracker.clearChangedLinesForWords([
  120. { lineNumber: 1 },
  121. { lineNumber: 2 },
  122. { lineNumber: 3 },
  123. ] as Word[])
  124. check([
  125. [1, false],
  126. [2, false],
  127. [3, false],
  128. ])
  129. // Overwrite the line plus some part of the second line
  130. const transaction = view.state.update({
  131. changes: [{ from: 0, to: doc[0].length + 3, insert: 'x' }],
  132. })
  133. view.dispatch(transaction)
  134. check([
  135. [1, true],
  136. [2, false],
  137. ])
  138. })
  139. it('should handle multiple changes', function () {
  140. lineTracker.clearChangedLinesForWords([
  141. { lineNumber: 1 },
  142. { lineNumber: 2 },
  143. { lineNumber: 3 },
  144. ] as Word[])
  145. check([
  146. [1, false],
  147. [2, false],
  148. [3, false],
  149. ])
  150. const transaction = view.state.update({
  151. changes: [
  152. { from: 0, insert: 'x' },
  153. { from: doc[0].length + 2, insert: 'xxxxx\nxxxxx\nxxxx' },
  154. ],
  155. })
  156. view.dispatch(transaction)
  157. check([
  158. [1, true],
  159. [2, true],
  160. [3, true],
  161. [4, false],
  162. [5, false],
  163. ])
  164. })
  165. it('should handle multiple deletions', function () {
  166. const transaction = view.state.update({
  167. changes: [
  168. { from: 0, to: 24, insert: '' },
  169. { from: 39, to: 44, insert: '' },
  170. ],
  171. })
  172. view.dispatch(transaction)
  173. check([
  174. [1, true],
  175. [2, true],
  176. ])
  177. })
  178. it('should handle multiple insertions', function () {
  179. const transactionUndo = view.state.update({
  180. changes: [
  181. { from: 0, to: 0, insert: 'big change\n'.repeat(20) },
  182. { from: 50, to: 50, insert: 'xxxx' },
  183. ],
  184. })
  185. view.dispatch(transactionUndo)
  186. })
  187. })
  188. })