rename-entity.test.js 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. import { expect } from 'chai'
  2. import sinon from 'sinon'
  3. import { screen, fireEvent } from '@testing-library/react'
  4. import fetchMock from 'fetch-mock'
  5. import MockedSocket from 'socket.io-mock'
  6. import {
  7. renderWithEditorContext,
  8. cleanUpContext,
  9. } from '../../../helpers/render-with-context'
  10. import FileTreeRoot from '../../../../../frontend/js/features/file-tree/components/file-tree-root'
  11. describe('FileTree Rename Entity Flow', function () {
  12. const onSelect = sinon.stub()
  13. const onInit = sinon.stub()
  14. beforeEach(function () {
  15. window.metaAttributesCache = new Map()
  16. window.metaAttributesCache.set('ol-user', { id: 'user1' })
  17. })
  18. afterEach(function () {
  19. fetchMock.restore()
  20. onSelect.reset()
  21. onInit.reset()
  22. cleanUpContext()
  23. window.metaAttributesCache = new Map()
  24. })
  25. beforeEach(function () {
  26. const rootFolder = [
  27. {
  28. _id: 'root-folder-id',
  29. name: 'rootFolder',
  30. docs: [{ _id: '456def', name: 'a.tex' }],
  31. folders: [
  32. {
  33. _id: '987jkl',
  34. name: 'folder',
  35. docs: [],
  36. fileRefs: [
  37. { _id: '789ghi', name: 'c.tex' },
  38. { _id: '981gkp', name: 'e.tex' },
  39. ],
  40. folders: [],
  41. },
  42. ],
  43. fileRefs: [],
  44. },
  45. ]
  46. renderWithEditorContext(
  47. <FileTreeRoot
  48. refProviders={{}}
  49. reindexReferences={() => null}
  50. setRefProviderEnabled={() => null}
  51. setStartedFreeTrial={() => null}
  52. onSelect={onSelect}
  53. onInit={onInit}
  54. isConnected
  55. />,
  56. {
  57. socket: new MockedSocket(),
  58. rootFolder,
  59. projectId: '123abc',
  60. }
  61. )
  62. onSelect.reset()
  63. })
  64. it('renames doc', function () {
  65. const fetchMatcher = /\/project\/\w+\/doc\/\w+\/rename/
  66. fetchMock.post(fetchMatcher, 204)
  67. const input = initItemRename('a.tex')
  68. fireEvent.change(input, { target: { value: 'b.tex' } })
  69. fireEvent.keyDown(input, { key: 'Enter' })
  70. screen.getByRole('treeitem', { name: 'b.tex' })
  71. const lastFetchBody = getLastFetchBody(fetchMatcher)
  72. expect(lastFetchBody.name).to.equal('b.tex')
  73. // onSelect should have been called once only: when the doc was selected for
  74. // rename
  75. sinon.assert.calledOnce(onSelect)
  76. })
  77. it('renames folder', function () {
  78. const fetchMatcher = /\/project\/\w+\/folder\/\w+\/rename/
  79. fetchMock.post(fetchMatcher, 204)
  80. const input = initItemRename('folder')
  81. fireEvent.change(input, { target: { value: 'new folder name' } })
  82. fireEvent.keyDown(input, { key: 'Enter' })
  83. screen.getByRole('treeitem', { name: 'new folder name' })
  84. const lastFetchBody = getLastFetchBody(fetchMatcher)
  85. expect(lastFetchBody.name).to.equal('new folder name')
  86. })
  87. it('renames file in subfolder', function () {
  88. const fetchMatcher = /\/project\/\w+\/file\/\w+\/rename/
  89. fetchMock.post(fetchMatcher, 204)
  90. const expandButton = screen.queryByRole('button', { name: 'Expand' })
  91. if (expandButton) fireEvent.click(expandButton)
  92. const input = initItemRename('c.tex')
  93. fireEvent.change(input, { target: { value: 'd.tex' } })
  94. fireEvent.keyDown(input, { key: 'Enter' })
  95. screen.getByRole('treeitem', { name: 'folder' })
  96. screen.getByRole('treeitem', { name: 'd.tex' })
  97. const lastFetchBody = getLastFetchBody(fetchMatcher)
  98. expect(lastFetchBody.name).to.equal('d.tex')
  99. })
  100. it('reverts rename on error', async function () {
  101. const fetchMatcher = /\/project\/\w+\/doc\/\w+\/rename/
  102. fetchMock.post(fetchMatcher, 500)
  103. const input = initItemRename('a.tex')
  104. fireEvent.change(input, { target: { value: 'b.tex' } })
  105. fireEvent.keyDown(input, { key: 'Enter' })
  106. screen.getByRole('treeitem', { name: 'b.tex' })
  107. })
  108. it('shows error modal on invalid filename', async function () {
  109. const input = initItemRename('a.tex')
  110. fireEvent.change(input, { target: { value: '///' } })
  111. fireEvent.keyDown(input, { key: 'Enter' })
  112. await screen.findByRole('alert', {
  113. name: 'File name is empty or contains invalid characters',
  114. hidden: true,
  115. })
  116. })
  117. it('shows error modal on duplicate filename', async function () {
  118. const input = initItemRename('a.tex')
  119. fireEvent.change(input, { target: { value: 'folder' } })
  120. fireEvent.keyDown(input, { key: 'Enter' })
  121. await screen.findByRole('alert', {
  122. name: 'A file or folder with this name already exists',
  123. hidden: true,
  124. })
  125. })
  126. it('shows error modal on duplicate filename in subfolder', async function () {
  127. const expandButton = screen.queryByRole('button', { name: 'Expand' })
  128. if (expandButton) fireEvent.click(expandButton)
  129. const input = initItemRename('c.tex')
  130. fireEvent.change(input, { target: { value: 'e.tex' } })
  131. fireEvent.keyDown(input, { key: 'Enter' })
  132. await screen.findByRole('alert', {
  133. name: 'A file or folder with this name already exists',
  134. hidden: true,
  135. })
  136. })
  137. it('shows error modal on blocked filename', async function () {
  138. const input = initItemRename('a.tex')
  139. fireEvent.change(input, { target: { value: 'prototype' } })
  140. fireEvent.keyDown(input, { key: 'Enter' })
  141. await screen.findByRole('alert', {
  142. name: 'This file name is blocked.',
  143. hidden: true,
  144. })
  145. })
  146. describe('via socket event', function () {
  147. it('renames doc', function () {
  148. screen.getByRole('treeitem', { name: 'a.tex' })
  149. window._ide.socket.socketClient.emit(
  150. 'reciveEntityRename',
  151. '456def',
  152. 'socket.tex'
  153. )
  154. screen.getByRole('treeitem', { name: 'socket.tex' })
  155. })
  156. })
  157. function initItemRename(treeitemName) {
  158. const treeitem = screen.getByRole('treeitem', { name: treeitemName })
  159. fireEvent.click(treeitem)
  160. const toggleButton = screen.getByRole('button', { name: 'Menu' })
  161. fireEvent.click(toggleButton)
  162. const renameButton = screen.getByRole('menuitem', { name: 'Rename' })
  163. fireEvent.click(renameButton)
  164. return screen.getByRole('textbox')
  165. }
  166. function getLastFetchBody(matcher) {
  167. const [, { body }] = fetchMock.lastCall(matcher)
  168. return JSON.parse(body)
  169. }
  170. })