file-tree-root.spec.tsx 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355
  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. import type { Socket } from '@/features/ide-react/connection/types/socket'
  5. describe('<FileTreeRoot/>', function () {
  6. beforeEach(function () {
  7. cy.window().then(win => {
  8. win.metaAttributesCache.set('ol-user', { id: 'user1' })
  9. })
  10. })
  11. it('renders', function () {
  12. const rootFolder = [
  13. {
  14. _id: 'root-folder-id',
  15. name: 'rootFolder',
  16. docs: [{ _id: '456def', name: 'main.tex' }],
  17. folders: [],
  18. fileRefs: [],
  19. },
  20. ]
  21. cy.mount(
  22. <EditorProviders
  23. rootFolder={rootFolder as any}
  24. projectId="123abc"
  25. rootDocId="456def"
  26. features={{} as any}
  27. permissionsLevel="owner"
  28. >
  29. <FileTreeRoot
  30. refProviders={{}}
  31. setRefProviderEnabled={cy.stub()}
  32. setStartedFreeTrial={cy.stub()}
  33. onSelect={cy.stub()}
  34. onInit={cy.stub()}
  35. isConnected
  36. />
  37. </EditorProviders>
  38. )
  39. cy.findByRole('tree')
  40. cy.findByRole('treeitem')
  41. cy.findByRole('treeitem', { name: 'main.tex', selected: true })
  42. cy.get('.disconnected-overlay').should('not.exist')
  43. })
  44. it('renders with invalid selected doc in local storage', function () {
  45. global.localStorage.setItem(
  46. 'doc.open_id.123abc',
  47. JSON.stringify('not-a-valid-id')
  48. )
  49. const rootFolder = [
  50. {
  51. _id: 'root-folder-id',
  52. name: 'rootFolder',
  53. docs: [{ _id: '456def', name: 'main.tex' }],
  54. folders: [],
  55. fileRefs: [],
  56. },
  57. ]
  58. cy.mount(
  59. <div style={{ width: 400 }}>
  60. <EditorProviders
  61. rootFolder={rootFolder as any}
  62. projectId="123abc"
  63. rootDocId="456def"
  64. features={{} as any}
  65. permissionsLevel="owner"
  66. >
  67. <FileTreeRoot
  68. refProviders={{}}
  69. setRefProviderEnabled={cy.stub()}
  70. setStartedFreeTrial={cy.stub()}
  71. onSelect={cy.stub()}
  72. onInit={cy.stub()}
  73. isConnected
  74. />
  75. </EditorProviders>
  76. </div>
  77. )
  78. // as a proxy to check that the invalid entity has not been select we start
  79. // a delete and ensure the modal is displayed (the cancel button can be
  80. // selected) This is needed to make sure the test fail.
  81. cy.findByRole('treeitem', { name: 'main.tex' }).click({
  82. ctrlKey: true,
  83. cmdKey: true,
  84. })
  85. cy.findByRole('button', { name: 'Open main.tex action menu' }).click()
  86. cy.findByRole('menuitem', { name: 'Delete' }).click()
  87. cy.findByRole('button', { name: 'Cancel' })
  88. })
  89. it('renders disconnected overlay', function () {
  90. const rootFolder = [
  91. {
  92. _id: 'root-folder-id',
  93. name: 'rootFolder',
  94. docs: [{ _id: '456def', name: 'main.tex' }],
  95. folders: [],
  96. fileRefs: [],
  97. },
  98. ]
  99. cy.mount(
  100. <EditorProviders
  101. rootFolder={rootFolder as any}
  102. projectId="123abc"
  103. rootDocId="456def"
  104. features={{} as any}
  105. permissionsLevel="owner"
  106. >
  107. <FileTreeRoot
  108. refProviders={{}}
  109. setRefProviderEnabled={cy.stub()}
  110. setStartedFreeTrial={cy.stub()}
  111. onSelect={cy.stub()}
  112. onInit={cy.stub()}
  113. isConnected={false}
  114. />
  115. </EditorProviders>
  116. )
  117. cy.get('.disconnected-overlay')
  118. })
  119. it('fire onSelect', function () {
  120. const rootFolder = [
  121. {
  122. _id: 'root-folder-id',
  123. name: 'rootFolder',
  124. docs: [
  125. { _id: '456def', name: 'main.tex' },
  126. { _id: '789ghi', name: 'other.tex' },
  127. ],
  128. folders: [],
  129. fileRefs: [],
  130. },
  131. ]
  132. cy.mount(
  133. <EditorProviders
  134. rootFolder={rootFolder as any}
  135. projectId="123abc"
  136. rootDocId="456def"
  137. features={{} as any}
  138. permissionsLevel="readOnly"
  139. >
  140. <FileTreeRoot
  141. refProviders={{}}
  142. setRefProviderEnabled={cy.stub()}
  143. setStartedFreeTrial={cy.stub()}
  144. onSelect={cy.stub().as('onSelect')}
  145. onInit={cy.stub()}
  146. isConnected
  147. />
  148. </EditorProviders>
  149. )
  150. cy.get('@onSelect').should('have.been.calledOnceWith', [
  151. Cypress.sinon.match({
  152. entity: Cypress.sinon.match({ _id: '456def', name: 'main.tex' }),
  153. }),
  154. ])
  155. cy.findByRole('tree')
  156. cy.findByRole('treeitem', { name: 'other.tex' }).click()
  157. cy.get('@onSelect').should('have.been.calledWith', [
  158. Cypress.sinon.match({
  159. entity: Cypress.sinon.match({ _id: '789ghi', name: 'other.tex' }),
  160. }),
  161. ])
  162. })
  163. it('only shows a menu button when a single item is selected', function () {
  164. const rootFolder = [
  165. {
  166. _id: 'root-folder-id',
  167. name: 'rootFolder',
  168. docs: [
  169. { _id: '456def', name: 'main.tex' },
  170. { _id: '789ghi', name: 'other.tex' },
  171. ],
  172. folders: [],
  173. fileRefs: [],
  174. },
  175. ]
  176. cy.mount(
  177. <EditorProviders
  178. rootFolder={rootFolder as any}
  179. projectId="123abc"
  180. rootDocId="456def"
  181. features={{} as any}
  182. permissionsLevel="owner"
  183. >
  184. <FileTreeRoot
  185. refProviders={{}}
  186. setRefProviderEnabled={cy.stub()}
  187. setStartedFreeTrial={cy.stub()}
  188. onSelect={cy.stub()}
  189. onInit={cy.stub()}
  190. isConnected
  191. />
  192. </EditorProviders>
  193. )
  194. cy.findByRole('treeitem', { name: 'main.tex', selected: true })
  195. cy.findByRole('treeitem', { name: 'other.tex', selected: false })
  196. // single item selected: menu button is visible
  197. cy.findAllByRole('button', { name: 'Open main.tex action menu' }).should(
  198. 'have.length',
  199. 1
  200. )
  201. // select the other item
  202. cy.findByRole('treeitem', { name: 'other.tex' }).click()
  203. cy.findByRole('treeitem', { name: 'main.tex', selected: false })
  204. cy.findByRole('treeitem', { name: 'other.tex', selected: true })
  205. // single item selected: menu button is visible
  206. cy.findAllByRole('button', { name: 'Open other.tex action menu' }).should(
  207. 'have.length',
  208. 1
  209. )
  210. // multi-select the main item
  211. cy.findByRole('treeitem', { name: 'main.tex' }).click({
  212. ctrlKey: true,
  213. cmdKey: true,
  214. })
  215. cy.findByRole('treeitem', { name: 'main.tex', selected: true })
  216. cy.findByRole('treeitem', { name: 'other.tex', selected: true })
  217. // multiple items selected: no menu button is visible
  218. cy.findAllByRole('button', { name: 'Open main.tex action menu' }).should(
  219. 'have.length',
  220. 0
  221. )
  222. })
  223. describe('when deselecting files', function () {
  224. let socket: SocketIOMock & Socket
  225. beforeEach(function () {
  226. socket = new SocketIOMock() as any
  227. const rootFolder = [
  228. {
  229. _id: 'root-folder-id',
  230. name: 'rootFolder',
  231. docs: [{ _id: '123abc', name: 'main.tex' }],
  232. folders: [
  233. {
  234. _id: '789ghi',
  235. name: 'thefolder',
  236. docs: [{ _id: '456def', name: 'sub.tex' }],
  237. fileRefs: [],
  238. folders: [],
  239. },
  240. ],
  241. fileRefs: [],
  242. },
  243. ]
  244. cy.mount(
  245. <EditorProviders
  246. rootFolder={rootFolder as any}
  247. projectId="123abc"
  248. rootDocId="456def"
  249. features={{} as any}
  250. permissionsLevel="owner"
  251. socket={socket}
  252. >
  253. <FileTreeRoot
  254. refProviders={{}}
  255. setRefProviderEnabled={cy.stub()}
  256. setStartedFreeTrial={cy.stub()}
  257. onSelect={cy.stub()}
  258. onInit={cy.stub()}
  259. isConnected
  260. />
  261. </EditorProviders>
  262. )
  263. // select the sub file
  264. cy.findByRole('treeitem', { name: 'sub.tex' }).click()
  265. cy.findByRole('treeitem', { name: 'sub.tex' }).should(
  266. 'have.attr',
  267. 'aria-selected',
  268. 'true'
  269. )
  270. // click on empty area (after giving it extra height below the tree)
  271. cy.findByTestId('file-tree-inner')
  272. .invoke('attr', 'style', 'height: 400px')
  273. .click()
  274. })
  275. it('removes the selected indicator', function () {
  276. cy.findByRole('treeitem', { selected: true }).should('not.exist')
  277. })
  278. it('disables the "rename" and "delete" buttons', function () {
  279. cy.findByRole('button', { name: 'Rename' }).should('not.exist')
  280. cy.findByRole('button', { name: 'Delete' }).should('not.exist')
  281. })
  282. it('creates new file in the root folder', function () {
  283. cy.intercept('project/*/doc', { statusCode: 200 })
  284. cy.findByRole('button', { name: /new file/i }).click()
  285. cy.findByRole('button', { name: /create/i }).click()
  286. cy.then(() => {
  287. socket.emitToClient('reciveNewDoc', 'root-folder-id', {
  288. _id: '12345',
  289. name: 'abcdef.tex',
  290. docs: [],
  291. fileRefs: [],
  292. folders: [],
  293. })
  294. })
  295. cy.findByRole('treeitem', { name: 'abcdef.tex' }).then($itemEl => {
  296. cy.findByTestId('file-tree-list-root').then($rootEl => {
  297. expect($itemEl.get(0).parentNode?.parentNode).to.equal($rootEl.get(0))
  298. })
  299. })
  300. })
  301. it('starts a new selection', function () {
  302. cy.findByRole('treeitem', { name: 'sub.tex' }).should(
  303. 'have.attr',
  304. 'aria-selected',
  305. 'false'
  306. )
  307. cy.findByRole('treeitem', { name: 'main.tex' }).click({
  308. ctrlKey: true,
  309. cmdKey: true,
  310. })
  311. cy.findByRole('treeitem', { name: 'main.tex' }).should(
  312. 'have.attr',
  313. 'aria-selected',
  314. 'true'
  315. )
  316. })
  317. })
  318. })