scroll-position.test.ts 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114
  1. import sinon from 'sinon'
  2. import { fireEvent, waitFor } from '@testing-library/react'
  3. import { expect } from 'chai'
  4. import { EditorView } from '@codemirror/view'
  5. import { EditorState } from '@codemirror/state'
  6. import {
  7. restoreScrollPosition,
  8. scrollPosition,
  9. } from '../../../../../frontend/js/features/source-editor/extensions/scroll-position'
  10. const doc = `
  11. \\documentclass{article}
  12. \\title{Your Paper}
  13. \\author{You}
  14. \\begin{document}
  15. \\maketitle
  16. \\begin{abstract}
  17. Your abstract.
  18. \\end{abstract}
  19. \\section{Introduction}
  20. Your introduction goes here!
  21. \\end{document}`
  22. const mockDoc = () => {
  23. return {
  24. doc_id: 'test-doc',
  25. }
  26. }
  27. describe('CodeMirror scroll position extension', function () {
  28. beforeEach(function () {
  29. sinon.stub(HTMLElement.prototype, 'scrollHeight').returns(800)
  30. sinon.stub(HTMLElement.prototype, 'scrollWidth').returns(500)
  31. sinon.stub(HTMLElement.prototype, 'clientHeight').returns(200)
  32. sinon.stub(HTMLElement.prototype, 'clientWidth').returns(500)
  33. sinon
  34. .stub(HTMLElement.prototype, 'getBoundingClientRect')
  35. .returns({ top: 100, left: 0, right: 500, bottom: 200 } as DOMRect)
  36. })
  37. afterEach(function () {
  38. sinon.restore()
  39. })
  40. it('stores scroll position when the view is destroyed', async function () {
  41. const currentDoc = mockDoc()
  42. sinon.stub(window.Storage.prototype, 'getItem').callsFake(key => {
  43. switch (key) {
  44. case 'doc.position.test-doc':
  45. return JSON.stringify({
  46. cursorPosition: { row: 2, column: 2 },
  47. firstVisibleLine: 5,
  48. })
  49. default:
  50. return null
  51. }
  52. })
  53. const view = new EditorView({
  54. state: EditorState.create({
  55. doc,
  56. extensions: [scrollPosition({ currentDoc }, { visual: false })],
  57. }),
  58. })
  59. const setItem = sinon.spy(window.Storage.prototype, 'setItem')
  60. fireEvent.scroll(view.scrollDOM, { target: { scrollTop: 10 } })
  61. view.destroy()
  62. const expected = JSON.stringify({
  63. cursorPosition: { row: 2, column: 2 },
  64. firstVisibleLine: 12,
  65. })
  66. await waitFor(() => {
  67. expect(setItem).to.have.been.calledWith('doc.position.test-doc', expected)
  68. })
  69. })
  70. it('restores scroll position', async function () {
  71. const currentDoc = mockDoc()
  72. const getItem = sinon
  73. .stub(window.Storage.prototype, 'getItem')
  74. .callsFake(key => {
  75. switch (key) {
  76. case 'editor.position.test-doc':
  77. return JSON.stringify({ firstVisibleLine: 12 })
  78. default:
  79. return null
  80. }
  81. })
  82. const view = new EditorView({
  83. state: EditorState.create({
  84. doc,
  85. extensions: [scrollPosition({ currentDoc }, { visual: false })],
  86. }),
  87. })
  88. view.dispatch(restoreScrollPosition())
  89. await waitFor(() => {
  90. expect(getItem).to.have.been.calledWith('doc.position.test-doc')
  91. })
  92. // TODO: scrollTop should be a higher value but requires more mocking
  93. // await waitFor(() => {
  94. // expect(view.scrollDOM.scrollTop).to.eq(0)
  95. // })
  96. })
  97. })