create-folder.test.js 7.7 KB

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