create-folder.test.js 7.6 KB

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