rename-entity.test.js 5.9 KB

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