file-tree-root.spec.tsx 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396
  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. <div style={{ width: 400 }}>
  61. <EditorProviders
  62. rootFolder={rootFolder as any}
  63. projectId="123abc"
  64. rootDocId="456def"
  65. features={{} as any}
  66. permissionsLevel="owner"
  67. >
  68. <FileTreeRoot
  69. refProviders={{}}
  70. reindexReferences={cy.stub()}
  71. setRefProviderEnabled={cy.stub()}
  72. setStartedFreeTrial={cy.stub()}
  73. onSelect={cy.stub()}
  74. onInit={cy.stub()}
  75. isConnected
  76. />
  77. </EditorProviders>
  78. </div>
  79. )
  80. // as a proxy to check that the invalid entity has not been select we start
  81. // a delete and ensure the modal is displayed (the cancel button can be
  82. // selected) This is needed to make sure the test fail.
  83. cy.findByRole('treeitem', { name: 'main.tex' }).click({
  84. ctrlKey: true,
  85. cmdKey: true,
  86. })
  87. cy.findByRole('button', { name: 'Menu' }).click()
  88. cy.findByRole('menuitem', { name: 'Delete' }).click()
  89. cy.findByRole('button', { name: 'Cancel' })
  90. })
  91. it('renders disconnected overlay', function () {
  92. const rootFolder = [
  93. {
  94. _id: 'root-folder-id',
  95. name: 'rootFolder',
  96. docs: [{ _id: '456def', name: 'main.tex' }],
  97. folders: [],
  98. fileRefs: [],
  99. },
  100. ]
  101. cy.mount(
  102. <EditorProviders
  103. rootFolder={rootFolder as any}
  104. projectId="123abc"
  105. rootDocId="456def"
  106. features={{} as any}
  107. permissionsLevel="owner"
  108. >
  109. <FileTreeRoot
  110. refProviders={{}}
  111. reindexReferences={cy.stub()}
  112. setRefProviderEnabled={cy.stub()}
  113. setStartedFreeTrial={cy.stub()}
  114. onSelect={cy.stub()}
  115. onInit={cy.stub()}
  116. isConnected={false}
  117. />
  118. </EditorProviders>
  119. )
  120. cy.get('.disconnected-overlay')
  121. })
  122. it('fire onSelect', function () {
  123. const rootFolder = [
  124. {
  125. _id: 'root-folder-id',
  126. name: 'rootFolder',
  127. docs: [
  128. { _id: '456def', name: 'main.tex' },
  129. { _id: '789ghi', name: 'other.tex' },
  130. ],
  131. folders: [],
  132. fileRefs: [],
  133. },
  134. ]
  135. cy.mount(
  136. <EditorProviders
  137. rootFolder={rootFolder as any}
  138. projectId="123abc"
  139. rootDocId="456def"
  140. features={{} as any}
  141. permissionsLevel="readOnly"
  142. >
  143. <FileTreeRoot
  144. refProviders={{}}
  145. reindexReferences={cy.stub()}
  146. setRefProviderEnabled={cy.stub()}
  147. setStartedFreeTrial={cy.stub()}
  148. onSelect={cy.stub().as('onSelect')}
  149. onInit={cy.stub()}
  150. isConnected
  151. />
  152. </EditorProviders>
  153. )
  154. cy.get('@onSelect').should('have.been.calledOnceWith', [
  155. Cypress.sinon.match({
  156. entity: Cypress.sinon.match({ _id: '456def', name: 'main.tex' }),
  157. }),
  158. ])
  159. cy.findByRole('tree')
  160. cy.findByRole('treeitem', { name: 'other.tex' }).click()
  161. cy.get('@onSelect').should('have.been.calledWith', [
  162. Cypress.sinon.match({
  163. entity: Cypress.sinon.match({ _id: '789ghi', name: 'other.tex' }),
  164. }),
  165. ])
  166. })
  167. it('listen to editor.openDoc', function () {
  168. const rootFolder = [
  169. {
  170. _id: 'root-folder-id',
  171. name: 'rootFolder',
  172. docs: [
  173. { _id: '456def', name: 'main.tex' },
  174. { _id: '789ghi', name: 'other.tex' },
  175. ],
  176. folders: [],
  177. fileRefs: [],
  178. },
  179. ]
  180. cy.mount(
  181. <EditorProviders
  182. rootFolder={rootFolder as any}
  183. projectId="123abc"
  184. rootDocId="456def"
  185. features={{} as any}
  186. permissionsLevel="readOnly"
  187. >
  188. <FileTreeRoot
  189. refProviders={{}}
  190. reindexReferences={cy.stub()}
  191. setRefProviderEnabled={cy.stub()}
  192. setStartedFreeTrial={cy.stub()}
  193. onSelect={cy.stub()}
  194. onInit={cy.stub()}
  195. isConnected
  196. />
  197. </EditorProviders>
  198. )
  199. cy.findByRole('treeitem', { name: 'main.tex', selected: true })
  200. // entities not found should be ignored
  201. cy.document().trigger('editor.openDoc', { detail: 'not-an-id' })
  202. cy.findByRole('treeitem', { name: 'main.tex', selected: true })
  203. cy.document().trigger('editor.openDoc', { detail: '789ghi' })
  204. cy.findByRole('treeitem', { name: 'main.tex', selected: false })
  205. cy.findByRole('treeitem', { name: 'other.tex', selected: true })
  206. })
  207. it('only shows a menu button when a single item is selected', function () {
  208. const rootFolder = [
  209. {
  210. _id: 'root-folder-id',
  211. name: 'rootFolder',
  212. docs: [
  213. { _id: '456def', name: 'main.tex' },
  214. { _id: '789ghi', name: 'other.tex' },
  215. ],
  216. folders: [],
  217. fileRefs: [],
  218. },
  219. ]
  220. cy.mount(
  221. <EditorProviders
  222. rootFolder={rootFolder as any}
  223. projectId="123abc"
  224. rootDocId="456def"
  225. features={{} as any}
  226. permissionsLevel="owner"
  227. >
  228. <FileTreeRoot
  229. refProviders={{}}
  230. reindexReferences={cy.stub()}
  231. setRefProviderEnabled={cy.stub()}
  232. setStartedFreeTrial={cy.stub()}
  233. onSelect={cy.stub()}
  234. onInit={cy.stub()}
  235. isConnected
  236. />
  237. </EditorProviders>
  238. )
  239. cy.findByRole('treeitem', { name: 'main.tex', selected: true })
  240. cy.findByRole('treeitem', { name: 'other.tex', selected: false })
  241. // single item selected: menu button is visible
  242. cy.findAllByRole('button', { name: 'Menu' }).should('have.length', 1)
  243. // select the other item
  244. cy.findByRole('treeitem', { name: 'other.tex' }).click()
  245. cy.findByRole('treeitem', { name: 'main.tex', selected: false })
  246. cy.findByRole('treeitem', { name: 'other.tex', selected: true })
  247. // single item selected: menu button is visible
  248. cy.findAllByRole('button', { name: 'Menu' }).should('have.length', 1)
  249. // multi-select the main item
  250. cy.findByRole('treeitem', { name: 'main.tex' }).click({
  251. ctrlKey: true,
  252. cmdKey: true,
  253. })
  254. cy.findByRole('treeitem', { name: 'main.tex', selected: true })
  255. cy.findByRole('treeitem', { name: 'other.tex', selected: true })
  256. // multiple items selected: no menu button is visible
  257. cy.findAllByRole('button', { name: 'Menu' }).should('have.length', 0)
  258. })
  259. describe('when deselecting files', function () {
  260. beforeEach(function () {
  261. const rootFolder = [
  262. {
  263. _id: 'root-folder-id',
  264. name: 'rootFolder',
  265. docs: [{ _id: '123abc', name: 'main.tex' }],
  266. folders: [
  267. {
  268. _id: '789ghi',
  269. name: 'thefolder',
  270. docs: [{ _id: '456def', name: 'sub.tex' }],
  271. fileRefs: [],
  272. folders: [],
  273. },
  274. ],
  275. fileRefs: [],
  276. },
  277. ]
  278. cy.mount(
  279. <EditorProviders
  280. rootFolder={rootFolder as any}
  281. projectId="123abc"
  282. rootDocId="456def"
  283. features={{} as any}
  284. permissionsLevel="owner"
  285. socket={new MockedSocket()}
  286. >
  287. <FileTreeRoot
  288. refProviders={{}}
  289. reindexReferences={cy.stub()}
  290. setRefProviderEnabled={cy.stub()}
  291. setStartedFreeTrial={cy.stub()}
  292. onSelect={cy.stub()}
  293. onInit={cy.stub()}
  294. isConnected
  295. />
  296. </EditorProviders>
  297. )
  298. // select the sub file
  299. cy.findByRole('treeitem', { name: 'sub.tex' }).click()
  300. cy.findByRole('treeitem', { name: 'sub.tex' }).should(
  301. 'have.attr',
  302. 'aria-selected',
  303. 'true'
  304. )
  305. // click on empty area (after giving it extra height below the tree)
  306. cy.findByTestId('file-tree-inner')
  307. .invoke('attr', 'style', 'height: 400px')
  308. .click()
  309. })
  310. it('removes the selected indicator', function () {
  311. cy.findByRole('treeitem', { selected: true }).should('not.exist')
  312. })
  313. it('disables the "rename" and "delete" buttons', function () {
  314. cy.findByRole('button', { name: 'Rename' }).should('not.exist')
  315. cy.findByRole('button', { name: 'Delete' }).should('not.exist')
  316. })
  317. it('creates new file in the root folder', function () {
  318. cy.intercept('project/*/doc', { statusCode: 200 })
  319. cy.findByRole('button', { name: /new file/i }).click()
  320. cy.findByRole('button', { name: /create/i }).click()
  321. cy.window().then(win => {
  322. // @ts-ignore
  323. win._ide.socket.socketClient.emit('reciveNewDoc', 'root-folder-id', {
  324. _id: '12345',
  325. name: 'abcdef.tex',
  326. docs: [],
  327. fileRefs: [],
  328. folders: [],
  329. })
  330. })
  331. cy.findByRole('treeitem', { name: 'abcdef.tex' }).then($itemEl => {
  332. cy.findByTestId('file-tree-list-root').then($rootEl => {
  333. expect($itemEl.get(0).parentNode).to.equal($rootEl.get(0))
  334. })
  335. })
  336. })
  337. it('starts a new selection', function () {
  338. cy.findByRole('treeitem', { name: 'sub.tex' }).should(
  339. 'have.attr',
  340. 'aria-selected',
  341. 'false'
  342. )
  343. cy.findByRole('treeitem', { name: 'main.tex' }).click({
  344. ctrlKey: true,
  345. cmdKey: true,
  346. })
  347. cy.findByRole('treeitem', { name: 'main.tex' }).should(
  348. 'have.attr',
  349. 'aria-selected',
  350. 'true'
  351. )
  352. })
  353. })
  354. })