create-folder.test.jsx 7.6 KB

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