create-folder.test.js 7.4 KB

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