rename-entity.test.js 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  1. import { expect } from 'chai'
  2. import sinon from 'sinon'
  3. import { screen, fireEvent } from '@testing-library/react'
  4. import fetchMock from 'fetch-mock'
  5. import MockedSocket from 'socket.io-mock'
  6. import {
  7. renderWithEditorContext,
  8. cleanUpContext,
  9. } from '../../../helpers/render-with-context'
  10. import FileTreeRoot from '../../../../../frontend/js/features/file-tree/components/file-tree-root'
  11. describe('FileTree Rename Entity Flow', function () {
  12. const onSelect = sinon.stub()
  13. const onInit = sinon.stub()
  14. beforeEach(function () {
  15. global.requestAnimationFrame = sinon.stub()
  16. })
  17. afterEach(function () {
  18. delete global.requestAnimationFrame
  19. fetchMock.restore()
  20. onSelect.reset()
  21. onInit.reset()
  22. cleanUpContext()
  23. })
  24. beforeEach(function () {
  25. const rootFolder = [
  26. {
  27. _id: 'root-folder-id',
  28. name: 'rootFolder',
  29. docs: [{ _id: '456def', name: 'a.tex' }],
  30. folders: [
  31. {
  32. _id: '987jkl',
  33. name: 'folder',
  34. docs: [],
  35. fileRefs: [
  36. { _id: '789ghi', name: 'c.tex' },
  37. { _id: '981gkp', name: 'e.tex' },
  38. ],
  39. folders: [],
  40. },
  41. ],
  42. fileRefs: [],
  43. },
  44. ]
  45. renderWithEditorContext(
  46. <FileTreeRoot
  47. refProviders={{}}
  48. reindexReferences={() => null}
  49. setRefProviderEnabled={() => null}
  50. setStartedFreeTrial={() => null}
  51. onSelect={onSelect}
  52. onInit={onInit}
  53. isConnected
  54. />,
  55. {
  56. socket: new MockedSocket(),
  57. rootFolder,
  58. projectId: '123abc',
  59. }
  60. )
  61. onSelect.reset()
  62. })
  63. it('renames doc', function () {
  64. const fetchMatcher = /\/project\/\w+\/doc\/\w+\/rename/
  65. fetchMock.post(fetchMatcher, 204)
  66. const input = initItemRename('a.tex')
  67. fireEvent.change(input, { target: { value: 'b.tex' } })
  68. fireEvent.keyDown(input, { key: 'Enter' })
  69. screen.getByRole('treeitem', { name: 'b.tex' })
  70. const lastFetchBody = getLastFetchBody(fetchMatcher)
  71. expect(lastFetchBody.name).to.equal('b.tex')
  72. // onSelect should have been called once only: when the doc was selected for
  73. // rename
  74. sinon.assert.calledOnce(onSelect)
  75. })
  76. it('renames folder', function () {
  77. const fetchMatcher = /\/project\/\w+\/folder\/\w+\/rename/
  78. fetchMock.post(fetchMatcher, 204)
  79. const input = initItemRename('folder')
  80. fireEvent.change(input, { target: { value: 'new folder name' } })
  81. fireEvent.keyDown(input, { key: 'Enter' })
  82. screen.getByRole('treeitem', { name: 'new folder name' })
  83. const lastFetchBody = getLastFetchBody(fetchMatcher)
  84. expect(lastFetchBody.name).to.equal('new folder name')
  85. })
  86. it('renames file in subfolder', function () {
  87. const fetchMatcher = /\/project\/\w+\/file\/\w+\/rename/
  88. fetchMock.post(fetchMatcher, 204)
  89. const expandButton = screen.queryByRole('button', { name: 'Expand' })
  90. if (expandButton) fireEvent.click(expandButton)
  91. const input = initItemRename('c.tex')
  92. fireEvent.change(input, { target: { value: 'd.tex' } })
  93. fireEvent.keyDown(input, { key: 'Enter' })
  94. screen.getByRole('treeitem', { name: 'folder' })
  95. screen.getByRole('treeitem', { name: 'd.tex' })
  96. const lastFetchBody = getLastFetchBody(fetchMatcher)
  97. expect(lastFetchBody.name).to.equal('d.tex')
  98. })
  99. it('reverts rename on error', async function () {
  100. const fetchMatcher = /\/project\/\w+\/doc\/\w+\/rename/
  101. fetchMock.post(fetchMatcher, 500)
  102. const input = initItemRename('a.tex')
  103. fireEvent.change(input, { target: { value: 'b.tex' } })
  104. fireEvent.keyDown(input, { key: 'Enter' })
  105. screen.getByRole('treeitem', { name: 'b.tex' })
  106. })
  107. it('shows error modal on invalid filename', async function () {
  108. const input = initItemRename('a.tex')
  109. fireEvent.change(input, { target: { value: '///' } })
  110. fireEvent.keyDown(input, { key: 'Enter' })
  111. await screen.findByRole('alert', {
  112. name: 'File name is empty or contains invalid characters',
  113. hidden: true,
  114. })
  115. })
  116. it('shows error modal on duplicate filename', async function () {
  117. const input = initItemRename('a.tex')
  118. fireEvent.change(input, { target: { value: 'folder' } })
  119. fireEvent.keyDown(input, { key: 'Enter' })
  120. await screen.findByRole('alert', {
  121. name: 'A file or folder with this name already exists',
  122. hidden: true,
  123. })
  124. })
  125. it('shows error modal on duplicate filename in subfolder', async function () {
  126. const expandButton = screen.queryByRole('button', { name: 'Expand' })
  127. if (expandButton) fireEvent.click(expandButton)
  128. const input = initItemRename('c.tex')
  129. fireEvent.change(input, { target: { value: 'e.tex' } })
  130. fireEvent.keyDown(input, { key: 'Enter' })
  131. await screen.findByRole('alert', {
  132. name: 'A file or folder with this name already exists',
  133. hidden: true,
  134. })
  135. })
  136. it('shows error modal on blocked filename', async function () {
  137. const input = initItemRename('a.tex')
  138. fireEvent.change(input, { target: { value: 'prototype' } })
  139. fireEvent.keyDown(input, { key: 'Enter' })
  140. await screen.findByRole('alert', {
  141. name: 'This file name is blocked.',
  142. hidden: true,
  143. })
  144. })
  145. describe('via socket event', function () {
  146. it('renames doc', function () {
  147. screen.getByRole('treeitem', { name: 'a.tex' })
  148. window._ide.socket.socketClient.emit(
  149. 'reciveEntityRename',
  150. '456def',
  151. 'socket.tex'
  152. )
  153. screen.getByRole('treeitem', { name: 'socket.tex' })
  154. })
  155. })
  156. function initItemRename(treeitemName) {
  157. const treeitem = screen.getByRole('treeitem', { name: treeitemName })
  158. fireEvent.click(treeitem)
  159. const toggleButton = screen.getByRole('button', { name: 'Menu' })
  160. fireEvent.click(toggleButton)
  161. const renameButton = screen.getByRole('menuitem', { name: 'Rename' })
  162. fireEvent.click(renameButton)
  163. return screen.getByRole('textbox')
  164. }
  165. function getLastFetchBody(matcher) {
  166. const [, { body }] = fetchMock.lastCall(matcher)
  167. return JSON.parse(body)
  168. }
  169. })