delete-entity.spec.tsx 8.2 KB

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