rename-entity.test.js 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  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. docs: [{ _id: '456def', name: 'a.tex' }],
  29. folders: [
  30. {
  31. _id: '987jkl',
  32. name: 'folder',
  33. docs: [],
  34. fileRefs: [
  35. { _id: '789ghi', name: 'c.tex' },
  36. { _id: '981gkp', name: 'e.tex' },
  37. ],
  38. folders: [],
  39. },
  40. ],
  41. fileRefs: [],
  42. },
  43. ]
  44. renderWithEditorContext(
  45. <FileTreeRoot
  46. rootFolder={rootFolder}
  47. projectId="123abc"
  48. hasWritePermissions
  49. userHasFeature={() => true}
  50. refProviders={{}}
  51. reindexReferences={() => null}
  52. setRefProviderEnabled={() => null}
  53. setStartedFreeTrial={() => null}
  54. onSelect={onSelect}
  55. onInit={onInit}
  56. isConnected
  57. />,
  58. { socket: new MockedSocket() }
  59. )
  60. })
  61. it('renames doc', function () {
  62. const fetchMatcher = /\/project\/\w+\/doc\/\w+\/rename/
  63. fetchMock.post(fetchMatcher, 204)
  64. const input = initItemRename('a.tex')
  65. fireEvent.change(input, { target: { value: 'b.tex' } })
  66. fireEvent.keyDown(input, { key: 'Enter' })
  67. screen.getByRole('treeitem', { name: 'b.tex' })
  68. const lastFetchBody = getLastFetchBody(fetchMatcher)
  69. expect(lastFetchBody.name).to.equal('b.tex')
  70. })
  71. it('renames folder', function () {
  72. const fetchMatcher = /\/project\/\w+\/folder\/\w+\/rename/
  73. fetchMock.post(fetchMatcher, 204)
  74. const input = initItemRename('folder')
  75. fireEvent.change(input, { target: { value: 'new folder name' } })
  76. fireEvent.keyDown(input, { key: 'Enter' })
  77. screen.getByRole('treeitem', { name: 'new folder name' })
  78. const lastFetchBody = getLastFetchBody(fetchMatcher)
  79. expect(lastFetchBody.name).to.equal('new folder name')
  80. })
  81. it('renames file in subfolder', function () {
  82. const fetchMatcher = /\/project\/\w+\/file\/\w+\/rename/
  83. fetchMock.post(fetchMatcher, 204)
  84. const expandButton = screen.queryByRole('button', { name: 'Expand' })
  85. if (expandButton) fireEvent.click(expandButton)
  86. const input = initItemRename('c.tex')
  87. fireEvent.change(input, { target: { value: 'd.tex' } })
  88. fireEvent.keyDown(input, { key: 'Enter' })
  89. screen.getByRole('treeitem', { name: 'folder' })
  90. screen.getByRole('treeitem', { name: 'd.tex' })
  91. const lastFetchBody = getLastFetchBody(fetchMatcher)
  92. expect(lastFetchBody.name).to.equal('d.tex')
  93. })
  94. it('reverts rename on error', async function () {
  95. const fetchMatcher = /\/project\/\w+\/doc\/\w+\/rename/
  96. fetchMock.post(fetchMatcher, 500)
  97. const input = initItemRename('a.tex')
  98. fireEvent.change(input, { target: { value: 'b.tex' } })
  99. fireEvent.keyDown(input, { key: 'Enter' })
  100. screen.getByRole('treeitem', { name: 'b.tex' })
  101. })
  102. it('shows error modal on invalid filename', async function () {
  103. const input = initItemRename('a.tex')
  104. fireEvent.change(input, { target: { value: '///' } })
  105. fireEvent.keyDown(input, { key: 'Enter' })
  106. await screen.findByRole('alert', {
  107. name: 'File name is empty or contains invalid characters',
  108. hidden: true,
  109. })
  110. })
  111. it('shows error modal on duplicate filename', async function () {
  112. const input = initItemRename('a.tex')
  113. fireEvent.change(input, { target: { value: 'folder' } })
  114. fireEvent.keyDown(input, { key: 'Enter' })
  115. await screen.findByRole('alert', {
  116. name: 'A file or folder with this name already exists',
  117. hidden: true,
  118. })
  119. })
  120. it('shows error modal on duplicate filename in subfolder', async function () {
  121. const expandButton = screen.queryByRole('button', { name: 'Expand' })
  122. if (expandButton) fireEvent.click(expandButton)
  123. const input = initItemRename('c.tex')
  124. fireEvent.change(input, { target: { value: 'e.tex' } })
  125. fireEvent.keyDown(input, { key: 'Enter' })
  126. await screen.findByRole('alert', {
  127. name: 'A file or folder with this name already exists',
  128. hidden: true,
  129. })
  130. })
  131. it('shows error modal on blocked filename', async function () {
  132. const input = initItemRename('a.tex')
  133. fireEvent.change(input, { target: { value: 'prototype' } })
  134. fireEvent.keyDown(input, { key: 'Enter' })
  135. await screen.findByRole('alert', {
  136. name: 'This file name is blocked.',
  137. hidden: true,
  138. })
  139. })
  140. describe('via socket event', function () {
  141. it('renames doc', function () {
  142. screen.getByRole('treeitem', { name: 'a.tex' })
  143. window._ide.socket.socketClient.emit(
  144. 'reciveEntityRename',
  145. '456def',
  146. 'socket.tex'
  147. )
  148. screen.getByRole('treeitem', { name: 'socket.tex' })
  149. })
  150. })
  151. function initItemRename(treeitemName) {
  152. const treeitem = screen.getByRole('treeitem', { name: treeitemName })
  153. fireEvent.click(treeitem)
  154. const toggleButton = screen.getByRole('button', { name: 'Menu' })
  155. fireEvent.click(toggleButton)
  156. const renameButton = screen.getByRole('menuitem', { name: 'Rename' })
  157. fireEvent.click(renameButton)
  158. return screen.getByRole('textbox')
  159. }
  160. function getLastFetchBody(matcher) {
  161. const [, { body }] = fetchMock.lastCall(matcher)
  162. return JSON.parse(body)
  163. }
  164. })