delete-entity.spec.tsx 8.5 KB

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