create-folder.test.js 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291
  1. import { expect } from 'chai'
  2. import sinon from 'sinon'
  3. import { screen, fireEvent, waitFor } 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 Create Folder 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. it('add to root when no files are selected', async function () {
  25. const rootFolder = [
  26. {
  27. _id: 'root-folder-id',
  28. docs: [{ _id: '456def', name: 'main.tex' }],
  29. folders: [],
  30. fileRefs: [],
  31. },
  32. ]
  33. renderWithEditorContext(
  34. <FileTreeRoot
  35. rootFolder={rootFolder}
  36. projectId="123abc"
  37. hasWritePermissions
  38. userHasFeature={() => true}
  39. refProviders={{}}
  40. reindexReferences={() => null}
  41. setRefProviderEnabled={() => null}
  42. setStartedFreeTrial={() => null}
  43. onSelect={onSelect}
  44. onInit={onInit}
  45. isConnected
  46. />,
  47. { socket: new MockedSocket() }
  48. )
  49. const newFolderName = 'Foo Bar In Root'
  50. const matcher = /\/project\/\w+\/folder/
  51. const response = {
  52. folders: [],
  53. fileRefs: [],
  54. docs: [],
  55. _id: fakeId(),
  56. name: newFolderName,
  57. }
  58. fetchMock.post(matcher, response)
  59. await fireCreateFolder(newFolderName)
  60. const lastCallBody = JSON.parse(fetchMock.lastCall(matcher)[1].body)
  61. expect(lastCallBody.name).to.equal(newFolderName)
  62. expect(lastCallBody.parent_folder_id).to.equal('root-folder-id')
  63. window._ide.socket.socketClient.emit('reciveNewFolder', 'root-folder-id', {
  64. _id: fakeId(),
  65. name: newFolderName,
  66. docs: [],
  67. fileRefs: [],
  68. folders: [],
  69. })
  70. await screen.findByRole('treeitem', { name: newFolderName })
  71. })
  72. it('add to folder from folder', async function () {
  73. const rootFolder = [
  74. {
  75. _id: 'root-folder-id',
  76. docs: [],
  77. folders: [
  78. {
  79. _id: '789ghi',
  80. name: 'thefolder',
  81. docs: [],
  82. fileRefs: [],
  83. folders: [],
  84. },
  85. ],
  86. fileRefs: [],
  87. },
  88. ]
  89. renderWithEditorContext(
  90. <FileTreeRoot
  91. rootFolder={rootFolder}
  92. projectId="123abc"
  93. hasWritePermissions
  94. userHasFeature={() => true}
  95. refProviders={{}}
  96. reindexReferences={() => null}
  97. setRefProviderEnabled={() => null}
  98. setStartedFreeTrial={() => null}
  99. rootDocId="789ghi"
  100. onSelect={onSelect}
  101. onInit={onInit}
  102. isConnected
  103. />,
  104. { socket: new MockedSocket() }
  105. )
  106. const expandButton = screen.getByRole('button', { name: 'Expand' })
  107. fireEvent.click(expandButton)
  108. const newFolderName = 'Foo Bar In thefolder'
  109. const matcher = /\/project\/\w+\/folder/
  110. const response = {
  111. folders: [],
  112. fileRefs: [],
  113. docs: [],
  114. _id: fakeId(),
  115. name: newFolderName,
  116. }
  117. fetchMock.post(matcher, response)
  118. await fireCreateFolder(newFolderName)
  119. const lastCallBody = JSON.parse(fetchMock.lastCall(matcher)[1].body)
  120. expect(lastCallBody.name).to.equal(newFolderName)
  121. expect(lastCallBody.parent_folder_id).to.equal('789ghi')
  122. window._ide.socket.socketClient.emit('reciveNewFolder', '789ghi', {
  123. _id: fakeId(),
  124. name: newFolderName,
  125. docs: [],
  126. fileRefs: [],
  127. folders: [],
  128. })
  129. // find the created folder
  130. await screen.findByRole('treeitem', { name: newFolderName })
  131. // collapse the parent folder; created folder should not be rendered anymore
  132. fireEvent.click(expandButton)
  133. expect(screen.queryByRole('treeitem', { name: newFolderName })).to.not.exist
  134. })
  135. it('add to folder from child', async function () {
  136. const rootFolder = [
  137. {
  138. _id: 'root-folder-id',
  139. docs: [],
  140. folders: [
  141. {
  142. _id: '789ghi',
  143. name: 'thefolder',
  144. docs: [],
  145. fileRefs: [{ _id: '456def', name: 'sub.tex' }],
  146. folders: [],
  147. },
  148. ],
  149. fileRefs: [],
  150. },
  151. ]
  152. renderWithEditorContext(
  153. <FileTreeRoot
  154. rootFolder={rootFolder}
  155. projectId="123abc"
  156. hasWritePermissions
  157. userHasFeature={() => true}
  158. refProviders={{}}
  159. reindexReferences={() => null}
  160. setRefProviderEnabled={() => null}
  161. setStartedFreeTrial={() => null}
  162. rootDocId="456def"
  163. onSelect={onSelect}
  164. onInit={onInit}
  165. isConnected
  166. />,
  167. { socket: new MockedSocket() }
  168. )
  169. const newFolderName = 'Foo Bar In thefolder'
  170. const matcher = /\/project\/\w+\/folder/
  171. const response = {
  172. folders: [],
  173. fileRefs: [],
  174. docs: [],
  175. _id: fakeId(),
  176. name: newFolderName,
  177. }
  178. fetchMock.post(matcher, response)
  179. await fireCreateFolder(newFolderName)
  180. const lastCallBody = JSON.parse(fetchMock.lastCall(matcher)[1].body)
  181. expect(lastCallBody.name).to.equal(newFolderName)
  182. expect(lastCallBody.parent_folder_id).to.equal('789ghi')
  183. window._ide.socket.socketClient.emit('reciveNewFolder', '789ghi', {
  184. _id: fakeId(),
  185. name: newFolderName,
  186. docs: [],
  187. fileRefs: [],
  188. folders: [],
  189. })
  190. // find the created folder
  191. await screen.findByRole('treeitem', { name: newFolderName })
  192. // collapse the parent folder; created folder should not be rendered anymore
  193. fireEvent.click(screen.getByRole('button', { name: 'Collapse' }))
  194. expect(screen.queryByRole('treeitem', { name: newFolderName })).to.not.exist
  195. })
  196. it('prevents adding duplicate or invalid names', async function () {
  197. const rootFolder = [
  198. {
  199. _id: 'root-folder-id',
  200. docs: [{ _id: '456def', name: 'existingFile' }],
  201. folders: [],
  202. fileRefs: [],
  203. },
  204. ]
  205. renderWithEditorContext(
  206. <FileTreeRoot
  207. rootFolder={rootFolder}
  208. projectId="123abc"
  209. hasWritePermissions
  210. userHasFeature={() => true}
  211. refProviders={{}}
  212. reindexReferences={() => null}
  213. setRefProviderEnabled={() => null}
  214. setStartedFreeTrial={() => null}
  215. rootDocId="456def"
  216. onSelect={onSelect}
  217. onInit={onInit}
  218. isConnected
  219. />,
  220. { socket: new MockedSocket() }
  221. )
  222. var newFolderName = 'existingFile'
  223. await fireCreateFolder(newFolderName)
  224. expect(fetchMock.called()).to.be.false
  225. await screen.findByRole('alert', {
  226. name: 'A file or folder with this name already exists',
  227. hidden: true,
  228. })
  229. newFolderName = 'in/valid '
  230. setFolderName(newFolderName)
  231. await screen.findByRole('alert', {
  232. name: 'File name is empty or contains invalid characters',
  233. hidden: true,
  234. })
  235. })
  236. async function fireCreateFolder(name) {
  237. const createFolderButton = screen.getByRole('button', {
  238. name: 'New Folder',
  239. })
  240. fireEvent.click(createFolderButton)
  241. setFolderName(name)
  242. const modalCreateButton = await getModalCreateButton()
  243. fireEvent.click(modalCreateButton)
  244. }
  245. function setFolderName(name) {
  246. const input = screen.getByRole('textbox')
  247. fireEvent.change(input, { target: { value: name } })
  248. }
  249. function fakeId() {
  250. return Math.random().toString(16).replace(/0\./, 'random-test-id-')
  251. }
  252. async function getModalCreateButton() {
  253. return waitFor(() => screen.getByRole('button', { name: 'Create' }))
  254. }
  255. })