delete-entity.spec.tsx 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283
  1. import FileTreeRoot from '../../../../../frontend/js/features/file-tree/components/file-tree-root'
  2. import { EditorProviders } from '../../../helpers/editor-providers'
  3. import { SocketIOMock } from '@/ide/connection/SocketIoShim'
  4. describe('FileTree Delete Entity Flow', function () {
  5. beforeEach(function () {
  6. cy.window().then(win => {
  7. win.metaAttributesCache.set('ol-user', { id: 'user1' })
  8. })
  9. })
  10. describe('single entity', function () {
  11. let socket: SocketIOMock
  12. beforeEach(function () {
  13. socket = new SocketIOMock()
  14. const rootFolder = [
  15. {
  16. _id: 'root-folder-id',
  17. name: 'rootFolder',
  18. docs: [
  19. { _id: '123abc', name: 'foo.tex' },
  20. { _id: '456def', name: 'main.tex' },
  21. ],
  22. folders: [],
  23. fileRefs: [],
  24. },
  25. ]
  26. cy.mount(
  27. <div style={{ width: 400 }}>
  28. <EditorProviders
  29. rootFolder={rootFolder as any}
  30. projectId="123abc"
  31. socket={socket}
  32. >
  33. <FileTreeRoot
  34. refProviders={{}}
  35. setRefProviderEnabled={cy.stub()}
  36. setStartedFreeTrial={cy.stub()}
  37. onSelect={cy.stub()}
  38. onInit={cy.stub()}
  39. isConnected
  40. />
  41. </EditorProviders>
  42. </div>
  43. )
  44. cy.findByRole('treeitem', { name: 'main.tex' }).click()
  45. cy.findByRole('button', { name: 'Menu' }).click()
  46. cy.findByRole('menuitem', { name: 'Delete' }).click()
  47. })
  48. it('removes item', function () {
  49. cy.intercept('delete', '/project/*/doc/*', { statusCode: 204 }).as(
  50. 'deleteDoc'
  51. )
  52. // check that the confirmation modal is open
  53. cy.findByText(
  54. 'Are you sure you want to permanently delete the following files?'
  55. )
  56. cy.findByRole('button', { name: 'Delete' }).click()
  57. cy.wait('@deleteDoc')
  58. cy.then(() => {
  59. socket.emitToClient('removeEntity', '456def')
  60. })
  61. cy.findByRole('treeitem', {
  62. name: 'main.tex',
  63. hidden: true, // treeitem might be hidden behind the modal
  64. }).should('not.exist')
  65. cy.findByRole('treeitem', {
  66. name: 'main.tex',
  67. }).should('not.exist')
  68. // check that the confirmation modal is closed
  69. cy.findByText(
  70. 'Are you sure you want to permanently delete the following files?'
  71. ).should('not.exist')
  72. cy.get('@deleteDoc.all').should('have.length', 1)
  73. })
  74. it('continues delete on 404s', function () {
  75. cy.intercept('delete', '/project/*/doc/*', { statusCode: 404 }).as(
  76. 'deleteDoc'
  77. )
  78. // check that the confirmation modal is open
  79. cy.findByText(
  80. 'Are you sure you want to permanently delete the following files?'
  81. )
  82. cy.findByRole('button', { name: 'Delete' }).click()
  83. cy.then(() => {
  84. socket.emitToClient('removeEntity', '456def')
  85. })
  86. cy.findByRole('treeitem', {
  87. name: 'main.tex',
  88. hidden: true, // treeitem might be hidden behind the modal
  89. }).should('not.exist')
  90. cy.findByRole('treeitem', {
  91. name: 'main.tex',
  92. }).should('not.exist')
  93. // check that the confirmation modal is closed
  94. // is not, the 404 probably triggered a bug
  95. cy.findByText(
  96. 'Are you sure you want to permanently delete the following files?'
  97. ).should('not.exist')
  98. })
  99. it('aborts delete on error', function () {
  100. cy.intercept('delete', '/project/*/doc/*', { statusCode: 500 }).as(
  101. 'deleteDoc'
  102. )
  103. cy.findByRole('button', { name: 'Delete' }).click()
  104. // The modal should still be open, but the file should not be deleted
  105. cy.findByRole('treeitem', { name: 'main.tex', hidden: true })
  106. })
  107. })
  108. describe('folders', function () {
  109. let socket: SocketIOMock
  110. beforeEach(function () {
  111. socket = new SocketIOMock()
  112. const rootFolder = [
  113. {
  114. _id: 'root-folder-id',
  115. name: 'rootFolder',
  116. docs: [{ _id: '456def', name: 'main.tex' }],
  117. folders: [
  118. {
  119. _id: '123abc',
  120. name: 'folder',
  121. docs: [],
  122. folders: [],
  123. fileRefs: [{ _id: '789ghi', name: 'my.bib' }],
  124. },
  125. ],
  126. fileRefs: [],
  127. },
  128. ]
  129. cy.mount(
  130. <div style={{ width: 400 }}>
  131. <EditorProviders
  132. rootFolder={rootFolder as any}
  133. projectId="123abc"
  134. socket={socket}
  135. >
  136. <FileTreeRoot
  137. refProviders={{}}
  138. setRefProviderEnabled={cy.stub()}
  139. setStartedFreeTrial={cy.stub()}
  140. onSelect={cy.stub()}
  141. onInit={cy.stub()}
  142. isConnected
  143. />
  144. </EditorProviders>
  145. </div>
  146. )
  147. cy.findByRole('button', { name: 'Expand' }).click()
  148. cy.findByRole('treeitem', { name: 'main.tex' }).click()
  149. cy.findByRole('treeitem', { name: 'my.bib' }).click({
  150. ctrlKey: true,
  151. cmdKey: true,
  152. })
  153. cy.then(() => {
  154. socket.emitToClient('removeEntity', '123abc')
  155. })
  156. })
  157. it('removes the folder', function () {
  158. cy.findByRole('treeitem', { name: 'folder' }).should('not.exist')
  159. })
  160. it('leaves the main file selected', function () {
  161. cy.findByRole('treeitem', { name: 'main.tex', selected: true })
  162. })
  163. it('unselect the child entity', function () {
  164. // as a proxy to check that the child entity has been unselect we start
  165. // a delete and ensure the modal is displayed (the cancel button can be
  166. // selected) This is needed to make sure the test fail.
  167. cy.findByRole('button', { name: 'Menu' }).click()
  168. cy.findByRole('menuitem', { name: 'Delete' }).click()
  169. cy.findByRole('button', { name: 'Cancel' })
  170. })
  171. })
  172. describe('multiple entities', function () {
  173. let socket: SocketIOMock
  174. beforeEach(function () {
  175. socket = new SocketIOMock()
  176. const rootFolder = [
  177. {
  178. _id: 'root-folder-id',
  179. name: 'rootFolder',
  180. docs: [{ _id: '456def', name: 'main.tex' }],
  181. folders: [],
  182. fileRefs: [{ _id: '789ghi', name: 'my.bib' }],
  183. },
  184. ]
  185. cy.mount(
  186. <div style={{ width: 400 }}>
  187. <EditorProviders
  188. rootFolder={rootFolder as any}
  189. projectId="123abc"
  190. socket={socket}
  191. >
  192. <FileTreeRoot
  193. refProviders={{}}
  194. setRefProviderEnabled={cy.stub()}
  195. setStartedFreeTrial={cy.stub()}
  196. onSelect={cy.stub()}
  197. onInit={cy.stub()}
  198. isConnected
  199. />
  200. </EditorProviders>
  201. </div>
  202. )
  203. // select two files
  204. cy.findByRole('treeitem', { name: 'main.tex' }).click()
  205. cy.findByRole('treeitem', { name: 'my.bib' }).click({
  206. ctrlKey: true,
  207. cmdKey: true,
  208. })
  209. // open the context menu
  210. cy.findByRole('button', { name: 'my.bib' }).trigger('contextmenu')
  211. // make sure the menu has opened, with only a "Delete" item (as multiple files are selected)
  212. cy.findByRole('menu')
  213. cy.findAllByRole('menuitem').should('have.length', 1)
  214. // select the Delete menu item
  215. cy.findByRole('menuitem', { name: 'Delete' }).click()
  216. })
  217. it('removes all items and reindexes references after deleting .bib file', function () {
  218. cy.intercept('delete', '/project/123abc/doc/456def', {
  219. statusCode: 204,
  220. }).as('deleteDoc')
  221. cy.intercept('delete', '/project/123abc/file/789ghi', {
  222. statusCode: 204,
  223. }).as('deleteFile')
  224. cy.findByRole('button', { name: 'Delete' }).click()
  225. cy.then(() => {
  226. socket.emitToClient('removeEntity', '456def')
  227. socket.emitToClient('removeEntity', '789ghi')
  228. })
  229. for (const name of ['main.tex', 'my.bib']) {
  230. for (const hidden of [true, false]) {
  231. cy.findByRole('treeitem', { name, hidden }).should('not.exist')
  232. }
  233. }
  234. // check that the confirmation modal is closed
  235. cy.findByText('Are you sure').should('not.exist')
  236. cy.get('@deleteDoc.all').should('have.length', 1)
  237. cy.get('@deleteFile.all').should('have.length', 1)
  238. })
  239. })
  240. })