delete-entity.spec.tsx 8.4 KB

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