rename-entity.test.js 5.5 KB

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