file-tree-root.spec.tsx 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394
  1. // @ts-ignore
  2. import MockedSocket from 'socket.io-mock'
  3. import FileTreeRoot from '../../../../../frontend/js/features/file-tree/components/file-tree-root'
  4. import { EditorProviders } from '../../../helpers/editor-providers'
  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. reindexReferences={cy.stub()}
  32. setRefProviderEnabled={cy.stub()}
  33. setStartedFreeTrial={cy.stub()}
  34. onSelect={cy.stub()}
  35. onInit={cy.stub()}
  36. isConnected
  37. />
  38. </EditorProviders>
  39. )
  40. cy.findByRole('tree')
  41. cy.findByRole('treeitem')
  42. cy.findByRole('treeitem', { name: 'main.tex', selected: true })
  43. cy.get('.disconnected-overlay').should('not.exist')
  44. })
  45. it('renders with invalid selected doc in local storage', function () {
  46. global.localStorage.setItem(
  47. 'doc.open_id.123abc',
  48. JSON.stringify('not-a-valid-id')
  49. )
  50. const rootFolder = [
  51. {
  52. _id: 'root-folder-id',
  53. name: 'rootFolder',
  54. docs: [{ _id: '456def', name: 'main.tex' }],
  55. folders: [],
  56. fileRefs: [],
  57. },
  58. ]
  59. cy.mount(
  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. reindexReferences={cy.stub()}
  70. setRefProviderEnabled={cy.stub()}
  71. setStartedFreeTrial={cy.stub()}
  72. onSelect={cy.stub()}
  73. onInit={cy.stub()}
  74. isConnected
  75. />
  76. </EditorProviders>
  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: '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. reindexReferences={cy.stub()}
  110. setRefProviderEnabled={cy.stub()}
  111. setStartedFreeTrial={cy.stub()}
  112. onSelect={cy.stub()}
  113. onInit={cy.stub()}
  114. isConnected={false}
  115. />
  116. </EditorProviders>
  117. )
  118. cy.get('.disconnected-overlay')
  119. })
  120. it('fire onSelect', function () {
  121. const rootFolder = [
  122. {
  123. _id: 'root-folder-id',
  124. name: 'rootFolder',
  125. docs: [
  126. { _id: '456def', name: 'main.tex' },
  127. { _id: '789ghi', name: 'other.tex' },
  128. ],
  129. folders: [],
  130. fileRefs: [],
  131. },
  132. ]
  133. cy.mount(
  134. <EditorProviders
  135. rootFolder={rootFolder as any}
  136. projectId="123abc"
  137. rootDocId="456def"
  138. features={{} as any}
  139. permissionsLevel="readOnly"
  140. >
  141. <FileTreeRoot
  142. refProviders={{}}
  143. reindexReferences={cy.stub()}
  144. setRefProviderEnabled={cy.stub()}
  145. setStartedFreeTrial={cy.stub()}
  146. onSelect={cy.stub().as('onSelect')}
  147. onInit={cy.stub()}
  148. isConnected
  149. />
  150. </EditorProviders>
  151. )
  152. cy.get('@onSelect').should('have.been.calledOnceWith', [
  153. Cypress.sinon.match({
  154. entity: Cypress.sinon.match({ _id: '456def', name: 'main.tex' }),
  155. }),
  156. ])
  157. cy.findByRole('tree')
  158. cy.findByRole('treeitem', { name: 'other.tex' }).click()
  159. cy.get('@onSelect').should('have.been.calledWith', [
  160. Cypress.sinon.match({
  161. entity: Cypress.sinon.match({ _id: '789ghi', name: 'other.tex' }),
  162. }),
  163. ])
  164. })
  165. it('listen to editor.openDoc', function () {
  166. const rootFolder = [
  167. {
  168. _id: 'root-folder-id',
  169. name: 'rootFolder',
  170. docs: [
  171. { _id: '456def', name: 'main.tex' },
  172. { _id: '789ghi', name: 'other.tex' },
  173. ],
  174. folders: [],
  175. fileRefs: [],
  176. },
  177. ]
  178. cy.mount(
  179. <EditorProviders
  180. rootFolder={rootFolder as any}
  181. projectId="123abc"
  182. rootDocId="456def"
  183. features={{} as any}
  184. permissionsLevel="readOnly"
  185. >
  186. <FileTreeRoot
  187. refProviders={{}}
  188. reindexReferences={cy.stub()}
  189. setRefProviderEnabled={cy.stub()}
  190. setStartedFreeTrial={cy.stub()}
  191. onSelect={cy.stub()}
  192. onInit={cy.stub()}
  193. isConnected
  194. />
  195. </EditorProviders>
  196. )
  197. cy.findByRole('treeitem', { name: 'main.tex', selected: true })
  198. // entities not found should be ignored
  199. cy.document().trigger('editor.openDoc', { detail: 'not-an-id' })
  200. cy.findByRole('treeitem', { name: 'main.tex', selected: true })
  201. cy.document().trigger('editor.openDoc', { detail: '789ghi' })
  202. cy.findByRole('treeitem', { name: 'main.tex', selected: false })
  203. cy.findByRole('treeitem', { name: 'other.tex', selected: true })
  204. })
  205. it('only shows a menu button when a single item is selected', function () {
  206. const rootFolder = [
  207. {
  208. _id: 'root-folder-id',
  209. name: 'rootFolder',
  210. docs: [
  211. { _id: '456def', name: 'main.tex' },
  212. { _id: '789ghi', name: 'other.tex' },
  213. ],
  214. folders: [],
  215. fileRefs: [],
  216. },
  217. ]
  218. cy.mount(
  219. <EditorProviders
  220. rootFolder={rootFolder as any}
  221. projectId="123abc"
  222. rootDocId="456def"
  223. features={{} as any}
  224. permissionsLevel="owner"
  225. >
  226. <FileTreeRoot
  227. refProviders={{}}
  228. reindexReferences={cy.stub()}
  229. setRefProviderEnabled={cy.stub()}
  230. setStartedFreeTrial={cy.stub()}
  231. onSelect={cy.stub()}
  232. onInit={cy.stub()}
  233. isConnected
  234. />
  235. </EditorProviders>
  236. )
  237. cy.findByRole('treeitem', { name: 'main.tex', selected: true })
  238. cy.findByRole('treeitem', { name: 'other.tex', selected: false })
  239. // single item selected: menu button is visible
  240. cy.findAllByRole('button', { name: 'Menu' }).should('have.length', 1)
  241. // select the other item
  242. cy.findByRole('treeitem', { name: 'other.tex' }).click()
  243. cy.findByRole('treeitem', { name: 'main.tex', selected: false })
  244. cy.findByRole('treeitem', { name: 'other.tex', selected: true })
  245. // single item selected: menu button is visible
  246. cy.findAllByRole('button', { name: 'Menu' }).should('have.length', 1)
  247. // multi-select the main item
  248. cy.findByRole('treeitem', { name: 'main.tex' }).click({
  249. ctrlKey: true,
  250. cmdKey: true,
  251. })
  252. cy.findByRole('treeitem', { name: 'main.tex', selected: true })
  253. cy.findByRole('treeitem', { name: 'other.tex', selected: true })
  254. // multiple items selected: no menu button is visible
  255. cy.findAllByRole('button', { name: 'Menu' }).should('have.length', 0)
  256. })
  257. describe('when deselecting files', function () {
  258. beforeEach(function () {
  259. const rootFolder = [
  260. {
  261. _id: 'root-folder-id',
  262. name: 'rootFolder',
  263. docs: [{ _id: '123abc', name: 'main.tex' }],
  264. folders: [
  265. {
  266. _id: '789ghi',
  267. name: 'thefolder',
  268. docs: [{ _id: '456def', name: 'sub.tex' }],
  269. fileRefs: [],
  270. folders: [],
  271. },
  272. ],
  273. fileRefs: [],
  274. },
  275. ]
  276. cy.mount(
  277. <EditorProviders
  278. rootFolder={rootFolder as any}
  279. projectId="123abc"
  280. rootDocId="456def"
  281. features={{} as any}
  282. permissionsLevel="owner"
  283. socket={new MockedSocket()}
  284. >
  285. <FileTreeRoot
  286. refProviders={{}}
  287. reindexReferences={cy.stub()}
  288. setRefProviderEnabled={cy.stub()}
  289. setStartedFreeTrial={cy.stub()}
  290. onSelect={cy.stub()}
  291. onInit={cy.stub()}
  292. isConnected
  293. />
  294. </EditorProviders>
  295. )
  296. // select the sub file
  297. cy.findByRole('treeitem', { name: 'sub.tex' }).click()
  298. cy.findByRole('treeitem', { name: 'sub.tex' }).should(
  299. 'have.attr',
  300. 'aria-selected',
  301. 'true'
  302. )
  303. // click on empty area (after giving it extra height below the tree)
  304. cy.findByTestId('file-tree-inner')
  305. .invoke('attr', 'style', 'height: 400px')
  306. .click()
  307. })
  308. it('removes the selected indicator', function () {
  309. cy.findByRole('treeitem', { selected: true }).should('not.exist')
  310. })
  311. it('disables the "rename" and "delete" buttons', function () {
  312. cy.findByRole('button', { name: 'Rename' }).should('not.exist')
  313. cy.findByRole('button', { name: 'Delete' }).should('not.exist')
  314. })
  315. it('creates new file in the root folder', function () {
  316. cy.intercept('project/*/doc', { statusCode: 200 })
  317. cy.findByRole('button', { name: /new file/i }).click()
  318. cy.findByRole('button', { name: /create/i }).click()
  319. cy.window().then(win => {
  320. // @ts-ignore
  321. win._ide.socket.socketClient.emit('reciveNewDoc', 'root-folder-id', {
  322. _id: '12345',
  323. name: 'abcdef.tex',
  324. docs: [],
  325. fileRefs: [],
  326. folders: [],
  327. })
  328. })
  329. cy.findByRole('treeitem', { name: 'abcdef.tex' }).then($itemEl => {
  330. cy.findByTestId('file-tree-list-root').then($rootEl => {
  331. expect($itemEl.get(0).parentNode).to.equal($rootEl.get(0))
  332. })
  333. })
  334. })
  335. it('starts a new selection', function () {
  336. cy.findByRole('treeitem', { name: 'sub.tex' }).should(
  337. 'have.attr',
  338. 'aria-selected',
  339. 'false'
  340. )
  341. cy.findByRole('treeitem', { name: 'main.tex' }).click({
  342. ctrlKey: true,
  343. cmdKey: true,
  344. })
  345. cy.findByRole('treeitem', { name: 'main.tex' }).should(
  346. 'have.attr',
  347. 'aria-selected',
  348. 'true'
  349. )
  350. })
  351. })
  352. })