create-folder.test.js 7.7 KB

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