From 005d7c55e253851c705db2e140525c2983998096 Mon Sep 17 00:00:00 2001 From: Wliu <50Wliu@users.noreply.github.com> Date: Thu, 10 May 2018 16:13:47 -0400 Subject: [PATCH 01/12] Copy files on drop if control is pressed --- lib/tree-view.coffee | 51 +++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 48 insertions(+), 3 deletions(-) diff --git a/lib/tree-view.coffee b/lib/tree-view.coffee index 2a3a4f76..d37c4c49 100644 --- a/lib/tree-view.coffee +++ b/lib/tree-view.coffee @@ -854,6 +854,44 @@ class TreeView pageDown: -> @element.scrollTop += @element.offsetHeight + copyEntry: (initialPath, newDirectoryPath) -> + if initialPath is newDirectoryPath + return + + entryName = path.basename(initialPath) + newPath = path.join(newDirectoryPath, entryName) + + # Duplicated from pasteEntries + initialPathIsDirectory = fs.isDirectorySync(initialPath) + fileCounter = 0 + originalNewPath = newPath + while fs.existsSync(newPath) + if initialPathIsDirectory + newPath = "#{originalNewPath}#{fileCounter}" + else + extension = getFullExtension(originalNewPath) + filePath = path.join(path.dirname(originalNewPath), path.basename(originalNewPath, extension)) + newPath = "#{filePath}#{fileCounter}#{extension}" + fileCounter += 1 + + try + console.log(initialPath, newPath) + @emitter.emit 'will-copy-entry', {initialPath, newPath} + if initialPathIsDirectory + fs.copySync(initialPath, newPath) + else + fs.makeTreeSync(newDirectoryPath) unless fs.existsSync(newDirectoryPath) + fs.writeFileSync(newPath, fs.readFileSync(initialPath)) + @emitter.emit 'entry-copied', {initialPath, newPath} + + if repo = repoForPath(newPath) + repo.getPathStatus(initialPath) + repo.getPathStatus(newPath) + + catch error + @emitter.emit 'copy-entry-failed', {initialPath, newPath} + atom.notifications.addWarning("Failed to copy entry #{initialPath} to #{newDirectoryPath}", detail: error.message) + moveEntry: (initialPath, newDirectoryPath) -> if initialPath is newDirectoryPath return @@ -1111,12 +1149,19 @@ class TreeView # iterate backwards so files in a dir are moved before the dir itself for initialPath in initialPaths by -1 @entryForPath(initialPath)?.collapse?() - @moveEntry(initialPath, newDirectoryPath) + if (process.platform is 'darwin' and e.metaKey) or e.ctrlKey + @copyEntry(initialPath, newDirectoryPath) + else + @moveEntry(initialPath, newDirectoryPath) else # Drop event from OS entry.classList.remove('selected') - for file in e.dataTransfer.files - @moveEntry(file.path, newDirectoryPath) + if (process.platform is 'darwin' and e.metaKey) or e.ctrlKey + for file in e.dataTransfer.files + @copyEntry(file.path, newDirectoryPath) + else + for file in e.dataTransfer.files + @moveEntry(file.path, newDirectoryPath) else if e.dataTransfer.files.length # Drop event from OS that isn't targeting a folder: add a new project folder atom.project.addPath(entry.path) for entry in e.dataTransfer.files From 637668fcd80eaba42b7415368d5cbbc13698d80d Mon Sep 17 00:00:00 2001 From: Wliu <50Wliu@users.noreply.github.com> Date: Tue, 19 Jun 2018 22:54:58 -0400 Subject: [PATCH 02/12] Merge copy logic --- lib/tree-view.coffee | 52 ++++++------------------------ spec/tree-view-package-spec.coffee | 2 +- 2 files changed, 10 insertions(+), 44 deletions(-) diff --git a/lib/tree-view.coffee b/lib/tree-view.coffee index d37c4c49..7aee7beb 100644 --- a/lib/tree-view.coffee +++ b/lib/tree-view.coffee @@ -727,42 +727,14 @@ class TreeView copiedPaths = if window.localStorage['tree-view:copyPath'] then JSON.parse(window.localStorage['tree-view:copyPath']) else null initialPaths = copiedPaths or cutPaths - catchAndShowFileErrors = (operation) -> - try - operation() - catch error - atom.notifications.addWarning("Unable to paste paths: #{initialPaths}", detail: error.message) - for initialPath in initialPaths ? [] - initialPathIsDirectory = fs.isDirectorySync(initialPath) if selectedEntry and initialPath and fs.existsSync(initialPath) basePath = selectedEntry.getPath() basePath = path.dirname(basePath) if selectedEntry.classList.contains('file') newPath = path.join(basePath, path.basename(initialPath)) if copiedPaths - # append a number to the file if an item with the same name exists - fileCounter = 0 - originalNewPath = newPath - while fs.existsSync(newPath) - if initialPathIsDirectory - newPath = "#{originalNewPath}#{fileCounter}" - else - extension = getFullExtension(originalNewPath) - filePath = path.join(path.dirname(originalNewPath), path.basename(originalNewPath, extension)) - newPath = "#{filePath}#{fileCounter}#{extension}" - fileCounter += 1 - - if fs.isDirectorySync(initialPath) - # use fs.copy to copy directories since read/write will fail for directories - catchAndShowFileErrors => - fs.copySync(initialPath, newPath) - @emitter.emit 'entry-copied', {initialPath, newPath} - else - # read the old file and write a new one at target location - catchAndShowFileErrors => - fs.writeFileSync(newPath, fs.readFileSync(initialPath)) - @emitter.emit 'entry-copied', {initialPath, newPath} + @copyEntry(initialPath, newPath) else if cutPaths # Only move the target if the cut target doesn't exist and if the newPath # is not within the initial path @@ -854,17 +826,11 @@ class TreeView pageDown: -> @element.scrollTop += @element.offsetHeight - copyEntry: (initialPath, newDirectoryPath) -> - if initialPath is newDirectoryPath - return - - entryName = path.basename(initialPath) - newPath = path.join(newDirectoryPath, entryName) - - # Duplicated from pasteEntries - initialPathIsDirectory = fs.isDirectorySync(initialPath) + copyEntry: (initialPath, newPath) -> + # append a number to the file if an item with the same name exists fileCounter = 0 originalNewPath = newPath + initialPathIsDirectory = fs.isDirectorySync(initialPath) while fs.existsSync(newPath) if initialPathIsDirectory newPath = "#{originalNewPath}#{fileCounter}" @@ -875,12 +841,10 @@ class TreeView fileCounter += 1 try - console.log(initialPath, newPath) @emitter.emit 'will-copy-entry', {initialPath, newPath} if initialPathIsDirectory fs.copySync(initialPath, newPath) else - fs.makeTreeSync(newDirectoryPath) unless fs.existsSync(newDirectoryPath) fs.writeFileSync(newPath, fs.readFileSync(initialPath)) @emitter.emit 'entry-copied', {initialPath, newPath} @@ -890,7 +854,7 @@ class TreeView catch error @emitter.emit 'copy-entry-failed', {initialPath, newPath} - atom.notifications.addWarning("Failed to copy entry #{initialPath} to #{newDirectoryPath}", detail: error.message) + atom.notifications.addWarning("Failed to copy entry #{initialPath} to #{newPath}", detail: error.message) moveEntry: (initialPath, newDirectoryPath) -> if initialPath is newDirectoryPath @@ -1148,9 +1112,10 @@ class TreeView # iterate backwards so files in a dir are moved before the dir itself for initialPath in initialPaths by -1 + continue if initialPath is newDirectoryPath @entryForPath(initialPath)?.collapse?() if (process.platform is 'darwin' and e.metaKey) or e.ctrlKey - @copyEntry(initialPath, newDirectoryPath) + @copyEntry(initialPath, path.join(newDirectoryPath, path.basename(initialPath))) else @moveEntry(initialPath, newDirectoryPath) else @@ -1158,7 +1123,8 @@ class TreeView entry.classList.remove('selected') if (process.platform is 'darwin' and e.metaKey) or e.ctrlKey for file in e.dataTransfer.files - @copyEntry(file.path, newDirectoryPath) + continue if file.path is newDirectoryPath + @copyEntry(file.path, path.join(newDirectoryPath, path.basename(file.path))) else for file in e.dataTransfer.files @moveEntry(file.path, newDirectoryPath) diff --git a/spec/tree-view-package-spec.coffee b/spec/tree-view-package-spec.coffee index 8c9d1b44..eda93f68 100644 --- a/spec/tree-view-package-spec.coffee +++ b/spec/tree-view-package-spec.coffee @@ -1911,7 +1911,7 @@ describe "TreeView", -> atom.notifications.clear() atom.commands.dispatch(treeView.element, "tree-view:paste") - expect(atom.notifications.getNotifications()[0].getMessage()).toContain 'Unable to paste paths' + expect(atom.notifications.getNotifications()[0].getMessage()).toContain 'Failed to copy entry' expect(atom.notifications.getNotifications()[0].getDetail()).toContain 'ENOENT: no such file or directory' describe "tree-view:add-file", -> From 8d118381e01e14dc3c0f98c71a445ca93bb364d3 Mon Sep 17 00:00:00 2001 From: Wliu <50Wliu@users.noreply.github.com> Date: Tue, 19 Jun 2018 23:17:04 -0400 Subject: [PATCH 03/12] :memo: accidentally removed comments --- lib/tree-view.coffee | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/tree-view.coffee b/lib/tree-view.coffee index 7aee7beb..cd2e2669 100644 --- a/lib/tree-view.coffee +++ b/lib/tree-view.coffee @@ -843,8 +843,10 @@ class TreeView try @emitter.emit 'will-copy-entry', {initialPath, newPath} if initialPathIsDirectory + # use fs.copy to copy directories since read/write will fail for directories fs.copySync(initialPath, newPath) else + # read the old file and write a new one at target location fs.writeFileSync(newPath, fs.readFileSync(initialPath)) @emitter.emit 'entry-copied', {initialPath, newPath} From 9bedb46c9b13ffba91628d0f7b34ac85799a5af5 Mon Sep 17 00:00:00 2001 From: Wliu <50Wliu@users.noreply.github.com> Date: Sat, 23 Jun 2018 12:26:22 -0400 Subject: [PATCH 04/12] Make tests more resilient if new files are added to fixtures --- spec/tree-view-package-spec.coffee | 70 ++++++++++++++++++++++-------- 1 file changed, 51 insertions(+), 19 deletions(-) diff --git a/spec/tree-view-package-spec.coffee b/spec/tree-view-package-spec.coffee index eda93f68..420819dc 100644 --- a/spec/tree-view-package-spec.coffee +++ b/spec/tree-view-package-spec.coffee @@ -3750,7 +3750,7 @@ describe "TreeView", -> expect(alphaDir).not.toHaveClass('selected') describe "when dropping a FileView onto a DirectoryView's header", -> - it "should move the file to the hovered directory", -> + fit "should move the file to the hovered directory", -> # Dragging delta.txt onto alphaDir alphaDir = findDirectoryContainingText(treeView.roots[0], 'alpha') alphaDir.expand() @@ -3759,6 +3759,9 @@ describe "TreeView", -> gammaDir.expand() deltaFile = gammaDir.entries.children[1] + alphaDirContents = findDirectoryContainingText(treeView.roots[0], 'alpha').querySelectorAll('.entry').length + gammaDirContents = findDirectoryContainingText(treeView.roots[0], 'gamma').querySelectorAll('.entry').length + [dragStartEvent, dragEnterEvent, dropEvent] = eventHelpers.buildInternalDragEvents([deltaFile], alphaDir.querySelector('.header'), alphaDir, treeView) @@ -3767,10 +3770,12 @@ describe "TreeView", -> expect(alphaDir.children.length).toBe 2 waitsFor "directory view contents to refresh", -> - findDirectoryContainingText(treeView.roots[0], 'alpha').querySelectorAll('.entry').length > 2 + findDirectoryContainingText(treeView.roots[0], 'alpha').querySelectorAll('.entry').length > alphaDirContents and + findDirectoryContainingText(treeView.roots[0], 'gamma').querySelectorAll('.entry').length < gammaDirContents runs -> - expect(findDirectoryContainingText(treeView.roots[0], 'alpha').querySelectorAll('.entry').length).toBe 3 + expect(findDirectoryContainingText(treeView.roots[0], 'alpha').querySelectorAll('.entry').length).toBe alphaDirContents + 1 + expect(findDirectoryContainingText(treeView.roots[0], 'gamma').querySelectorAll('.entry').length).toBe gammaDirContents - 1 it "shouldn't update editors with similar file paths", -> deltaFilePath2 = path.join(gammaDirPath, 'delta.txt2') @@ -3813,6 +3818,9 @@ describe "TreeView", -> gammaDir.expand() deltaFile = gammaDir.entries.children[1] + alphaDirContents = findDirectoryContainingText(treeView.roots[0], 'alpha').querySelectorAll('.entry').length + gammaDirContents = findDirectoryContainingText(treeView.roots[0], 'gamma').querySelectorAll('.entry').length + [dragStartEvent, dragEnterEvent, dropEvent] = eventHelpers.buildInternalDragEvents([deltaFile], betaFile, alphaDir, treeView) @@ -3821,10 +3829,12 @@ describe "TreeView", -> expect(alphaDir.children.length).toBe 2 waitsFor "directory view contents to refresh", -> - findDirectoryContainingText(treeView.roots[0], 'alpha').querySelectorAll('.entry').length > 2 + findDirectoryContainingText(treeView.roots[0], 'alpha').querySelectorAll('.entry').length > alphaDirContents and + findDirectoryContainingText(treeView.roots[0], 'gamma').querySelectorAll('.entry').length < gammaDirContents runs -> - expect(findDirectoryContainingText(treeView.roots[0], 'alpha').querySelectorAll('.entry').length).toBe 3 + expect(findDirectoryContainingText(treeView.roots[0], 'alpha').querySelectorAll('.entry').length).toBe alphaDirContents + 1 + expect(findDirectoryContainingText(treeView.roots[0], 'gamma').querySelectorAll('.entry').length).toBe gammaDirContents - 1 it "shouldn't update editors with similar file paths", -> deltaFilePath2 = path.join(gammaDirPath, 'delta.txt2') @@ -3859,7 +3869,7 @@ describe "TreeView", -> describe "when dropping multiple FileViews onto a DirectoryView's header", -> it "should move the files to the hovered directory", -> - # Dragging delta.txt onto alphaDir + # Dragging multiple files in gammaDir onto alphaDir alphaDir = findDirectoryContainingText(treeView.roots[0], 'alpha') alphaDir.expand() @@ -3867,6 +3877,9 @@ describe "TreeView", -> gammaDir.expand() gammaFiles = [].slice.call(gammaDir.entries.children, 1, 3) + alphaDirContents = findDirectoryContainingText(treeView.roots[0], 'alpha').querySelectorAll('.entry').length + gammaDirContents = findDirectoryContainingText(treeView.roots[0], 'gamma').querySelectorAll('.entry').length + [dragStartEvent, dragEnterEvent, dropEvent] = eventHelpers.buildInternalDragEvents(gammaFiles, alphaDir.querySelector('.header'), alphaDir, treeView) @@ -3876,10 +3889,12 @@ describe "TreeView", -> expect(alphaDir.entries.children.length).toBe 2 waitsFor "directory view contents to refresh", -> - findDirectoryContainingText(treeView.roots[0], 'alpha').querySelectorAll('.entry').length > 2 + findDirectoryContainingText(treeView.roots[0], 'alpha').querySelectorAll('.entry').length > alphaDirContents and + findDirectoryContainingText(treeView.roots[0], 'gamma').querySelectorAll('.entry').length < gammaDirContents runs -> - expect(findDirectoryContainingText(treeView.roots[0], 'alpha').querySelectorAll('.entry').length).toBe 4 + expect(findDirectoryContainingText(treeView.roots[0], 'alpha').querySelectorAll('.entry').length).toBe alphaDirContents + 2 + expect(findDirectoryContainingText(treeView.roots[0], 'gamma').querySelectorAll('.entry').length).toBe gammaDirContents - 2 describe "when dropping a DirectoryView and FileViews onto a DirectoryView's header", -> it "should move the files and directory to the hovered directory", -> @@ -3893,6 +3908,9 @@ describe "TreeView", -> thetaDir = findDirectoryContainingText(treeView.roots[0], 'theta') thetaDir.expand() + alphaDirContents = findDirectoryContainingText(treeView.roots[0], 'alpha').querySelectorAll('.entry').length + thetaDirContents = findDirectoryContainingText(treeView.roots[0], 'theta').querySelectorAll('.entry').length + dragged = [alphaFile, alphaDir] [dragStartEvent, dragEnterEvent, dropEvent] = @@ -3904,15 +3922,15 @@ describe "TreeView", -> expect(thetaDir.children.length).toBe 2 waitsFor "directory view contents to refresh", -> - findDirectoryContainingText(treeView.roots[0], 'theta').querySelectorAll('.entry').length > 2 + findDirectoryContainingText(treeView.roots[0], 'theta').querySelectorAll('.entry').length > thetaDirContents runs -> thetaDir.expand() - expect(thetaDir.querySelectorAll('.entry').length).toBe 3 + expect(thetaDir.querySelectorAll('.entry').length).toBe thetaDirContents + 2 # alpha dir still has all its entries alphaDir = findDirectoryContainingText(thetaDir.entries, 'alpha') alphaDir.expand() - expect(alphaDir.querySelectorAll('.entry').length).toBe 2 + expect(alphaDir.querySelectorAll('.entry').length).toBe alphaDirContents describe "when dropping a DirectoryView onto a DirectoryView's header", -> beforeEach -> @@ -3929,6 +3947,9 @@ describe "TreeView", -> thetaDir = gammaDir.entries.children[0] thetaDir.expand() + alphaDirContents = findDirectoryContainingText(treeView.roots[0], 'alpha').querySelectorAll('.entry').length + thetaDirContents = findDirectoryContainingText(treeView.roots[0], 'theta').querySelectorAll('.entry').length + [dragStartEvent, dragEnterEvent, dropEvent] = eventHelpers.buildInternalDragEvents([thetaDir], alphaDir.querySelector('.header'), alphaDir, treeView) treeView.onDragStart(dragStartEvent) @@ -3936,10 +3957,15 @@ describe "TreeView", -> expect(alphaDir.children.length).toBe 2 waitsFor "directory view contents to refresh", -> - findDirectoryContainingText(treeView.roots[0], 'alpha').querySelectorAll('.entry').length > 2 + findDirectoryContainingText(treeView.roots[0], 'alpha').querySelectorAll('.entry').length > alphaDirContents runs -> - expect(findDirectoryContainingText(treeView.roots[0], 'alpha').querySelectorAll('.entry').length).toBe 3 + expect(findDirectoryContainingText(treeView.roots[0], 'alpha').querySelectorAll('.entry').length).toBe alphaDirContents + 1 + + thetaDir = findDirectoryContainingText(alphaDir.entries, 'theta') + thetaDir.expand() + expect(thetaDir.querySelectorAll('.entry').length).toBe thetaDirContents + editor = atom.workspace.getActiveTextEditor() expect(editor.getPath()).toBe(thetaFilePath.replace('gamma', 'alpha')) @@ -4000,6 +4026,8 @@ describe "TreeView", -> alphaDir = findDirectoryContainingText(treeView.roots[0], 'alpha') alphaDir.expand() + alphaDirContents = findDirectoryContainingText(treeView.roots[0], 'alpha').querySelectorAll('.entry').length + dropEvent = eventHelpers.buildExternalDropEvent([deltaFilePath], alphaDir) runs -> @@ -4007,10 +4035,10 @@ describe "TreeView", -> expect(alphaDir.children.length).toBe 2 waitsFor "directory view contents to refresh", -> - findDirectoryContainingText(treeView.roots[0], 'alpha').querySelectorAll('.entry').length > 2 + findDirectoryContainingText(treeView.roots[0], 'alpha').querySelectorAll('.entry').length > alphaDirContents runs -> - expect(findDirectoryContainingText(treeView.roots[0], 'alpha').querySelectorAll('.entry').length).toBe 3 + expect(findDirectoryContainingText(treeView.roots[0], 'alpha').querySelectorAll('.entry').length).toBe alphaDirContents + 1 describe "when dragging a directory from the OS onto a DirectoryView's header", -> it "should move the directory to the hovered directory", -> @@ -4018,15 +4046,17 @@ describe "TreeView", -> alphaDir = findDirectoryContainingText(treeView.roots[0], 'alpha') alphaDir.expand() + alphaDirContents = findDirectoryContainingText(treeView.roots[0], 'alpha').querySelectorAll('.entry').length + dropEvent = eventHelpers.buildExternalDropEvent([gammaDirPath], alphaDir) treeView.onDrop(dropEvent) expect(alphaDir.children.length).toBe 2 waitsFor "directory view contents to refresh", -> - findDirectoryContainingText(treeView.roots[0], 'alpha').querySelectorAll('.entry').length > 2 + findDirectoryContainingText(treeView.roots[0], 'alpha').querySelectorAll('.entry').length > alphaDirContents runs -> - expect(findDirectoryContainingText(treeView.roots[0], 'alpha').querySelectorAll('.entry').length).toBe 3 + expect(findDirectoryContainingText(treeView.roots[0], 'alpha').querySelectorAll('.entry').length).toBe alphaDirContents + 1 describe "when dragging a file and directory from the OS onto a DirectoryView's header", -> it "should move the file and directory to the hovered directory", -> @@ -4034,6 +4064,8 @@ describe "TreeView", -> alphaDir = findDirectoryContainingText(treeView.roots[0], 'alpha') alphaDir.expand() + alphaDirContents = findDirectoryContainingText(treeView.roots[0], 'alpha').querySelectorAll('.entry').length + dropEvent = eventHelpers.buildExternalDropEvent([deltaFilePath, gammaDirPath], alphaDir) runs -> @@ -4041,10 +4073,10 @@ describe "TreeView", -> expect(alphaDir.children.length).toBe 2 waitsFor "directory view contents to refresh", -> - findDirectoryContainingText(treeView.roots[0], 'alpha').querySelectorAll('.entry').length > 3 + findDirectoryContainingText(treeView.roots[0], 'alpha').querySelectorAll('.entry').length > alphaDirContents runs -> - expect(findDirectoryContainingText(treeView.roots[0], 'alpha').querySelectorAll('.entry').length).toBe 4 + expect(findDirectoryContainingText(treeView.roots[0], 'alpha').querySelectorAll('.entry').length).toBe alphaDirContents + 2 describe "when dragging a directory from the OS onto a blank section of the Tree View", -> it "should create a new project folder", -> From 2e9e757693208c4058f26fce148f39cd7ca51ee3 Mon Sep 17 00:00:00 2001 From: Wliu <50Wliu@users.noreply.github.com> Date: Sat, 23 Jun 2018 12:39:59 -0400 Subject: [PATCH 05/12] Un-fit it --- spec/tree-view-package-spec.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/tree-view-package-spec.coffee b/spec/tree-view-package-spec.coffee index 420819dc..4610ed4f 100644 --- a/spec/tree-view-package-spec.coffee +++ b/spec/tree-view-package-spec.coffee @@ -3750,7 +3750,7 @@ describe "TreeView", -> expect(alphaDir).not.toHaveClass('selected') describe "when dropping a FileView onto a DirectoryView's header", -> - fit "should move the file to the hovered directory", -> + it "should move the file to the hovered directory", -> # Dragging delta.txt onto alphaDir alphaDir = findDirectoryContainingText(treeView.roots[0], 'alpha') alphaDir.expand() From b77d1c5962ac898b72ed76be85d4eafc003d8a87 Mon Sep 17 00:00:00 2001 From: Wliu <50Wliu@users.noreply.github.com> Date: Tue, 26 Jun 2018 22:04:46 -0400 Subject: [PATCH 06/12] Add specs for new behavior --- spec/event-helpers.coffee | 10 ++++-- spec/tree-view-package-spec.coffee | 49 ++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 2 deletions(-) diff --git a/spec/event-helpers.coffee b/spec/event-helpers.coffee index 16af1291..3efe4533 100644 --- a/spec/event-helpers.coffee +++ b/spec/event-helpers.coffee @@ -1,4 +1,4 @@ -module.exports.buildInternalDragEvents = (dragged, enterTarget, dropTarget, treeView) -> +module.exports.buildInternalDragEvents = (dragged, enterTarget, dropTarget, treeView, copy = false) -> dataTransfer = data: {} setData: (key, value) -> @data[key] = "#{value}" # Drag events stringify data values @@ -25,6 +25,9 @@ module.exports.buildInternalDragEvents = (dragged, enterTarget, dropTarget, tree Object.defineProperty(dropEvent, 'target', value: dropTarget) Object.defineProperty(dropEvent, 'currentTarget', value: dropTarget) Object.defineProperty(dropEvent, 'dataTransfer', value: dataTransfer) + if copy + key = if process.platform is 'darwin' then 'metaKey' else 'ctrlKey' + Object.defineProperty(dropEvent, key, value: true) dragEnterEvent = new DragEvent('dragenter') Object.defineProperty(dragEnterEvent, 'target', value: enterTarget) @@ -33,7 +36,7 @@ module.exports.buildInternalDragEvents = (dragged, enterTarget, dropTarget, tree [dragStartEvent, dragEnterEvent, dropEvent] -module.exports.buildExternalDropEvent = (filePaths, dropTarget) -> +module.exports.buildExternalDropEvent = (filePaths, dropTarget, copy = false) -> dataTransfer = data: {} setData: (key, value) -> @data[key] = "#{value}" # Drag events stringify data values @@ -51,6 +54,9 @@ module.exports.buildExternalDropEvent = (filePaths, dropTarget) -> Object.defineProperty(dropEvent, 'target', value: dropTarget) Object.defineProperty(dropEvent, 'currentTarget', value: dropTarget) Object.defineProperty(dropEvent, 'dataTransfer', value: dataTransfer) + if copy + key = if process.platform is 'darwin' then 'metaKey' else 'ctrlKey' + Object.defineProperty(dropEvent, key, value: true) for filePath in filePaths dropEvent.dataTransfer.files.push({path: filePath}) diff --git a/spec/tree-view-package-spec.coffee b/spec/tree-view-package-spec.coffee index 4610ed4f..bd6383dd 100644 --- a/spec/tree-view-package-spec.coffee +++ b/spec/tree-view-package-spec.coffee @@ -3777,6 +3777,33 @@ describe "TreeView", -> expect(findDirectoryContainingText(treeView.roots[0], 'alpha').querySelectorAll('.entry').length).toBe alphaDirContents + 1 expect(findDirectoryContainingText(treeView.roots[0], 'gamma').querySelectorAll('.entry').length).toBe gammaDirContents - 1 + describe 'when the ctrl/cmd modifier key is pressed', -> + it "should copy the file to the hovered directory", -> + # Dragging delta.txt onto alphaDir + alphaDir = findDirectoryContainingText(treeView.roots[0], 'alpha') + alphaDir.expand() + + gammaDir = findDirectoryContainingText(treeView.roots[0], 'gamma') + gammaDir.expand() + deltaFile = gammaDir.entries.children[1] + + alphaDirContents = findDirectoryContainingText(treeView.roots[0], 'alpha').querySelectorAll('.entry').length + gammaDirContents = findDirectoryContainingText(treeView.roots[0], 'gamma').querySelectorAll('.entry').length + + [dragStartEvent, dragEnterEvent, dropEvent] = + eventHelpers.buildInternalDragEvents([deltaFile], alphaDir.querySelector('.header'), alphaDir, treeView, true) + + treeView.onDragStart(dragStartEvent) + treeView.onDrop(dropEvent) + expect(alphaDir.children.length).toBe 2 + + waitsFor "directory view contents to refresh", -> + findDirectoryContainingText(treeView.roots[0], 'alpha').querySelectorAll('.entry').length > alphaDirContents + + runs -> + expect(findDirectoryContainingText(treeView.roots[0], 'alpha').querySelectorAll('.entry').length).toBe alphaDirContents + 1 + expect(findDirectoryContainingText(treeView.roots[0], 'gamma').querySelectorAll('.entry').length).toBe gammaDirContents + it "shouldn't update editors with similar file paths", -> deltaFilePath2 = path.join(gammaDirPath, 'delta.txt2') fs.writeFileSync(deltaFilePath2, 'copy') @@ -4039,6 +4066,28 @@ describe "TreeView", -> runs -> expect(findDirectoryContainingText(treeView.roots[0], 'alpha').querySelectorAll('.entry').length).toBe alphaDirContents + 1 + expect(fs.existsSync(deltaFilePath)).toBe false + + describe "when the ctrl/cmd modifier key is pressed", -> + it "should copy the file to the hovered directory", -> + # Dragging delta.txt from OS file explorer onto alphaDir + alphaDir = findDirectoryContainingText(treeView.roots[0], 'alpha') + alphaDir.expand() + + alphaDirContents = findDirectoryContainingText(treeView.roots[0], 'alpha').querySelectorAll('.entry').length + + dropEvent = eventHelpers.buildExternalDropEvent([deltaFilePath], alphaDir, true) + + runs -> + treeView.onDrop(dropEvent) + expect(alphaDir.children.length).toBe 2 + + waitsFor "directory view contents to refresh", -> + findDirectoryContainingText(treeView.roots[0], 'alpha').querySelectorAll('.entry').length > alphaDirContents + + runs -> + expect(findDirectoryContainingText(treeView.roots[0], 'alpha').querySelectorAll('.entry').length).toBe alphaDirContents + 1 + expect(fs.existsSync(deltaFilePath)).toBe true describe "when dragging a directory from the OS onto a DirectoryView's header", -> it "should move the directory to the hovered directory", -> From f82f22c72cccdae45099e1b17a5f5f587681cb80 Mon Sep 17 00:00:00 2001 From: Wliu <50Wliu@users.noreply.github.com> Date: Tue, 26 Jun 2018 22:51:24 -0400 Subject: [PATCH 07/12] Merge moveEntry logic and simplify implementations --- lib/tree-view.coffee | 52 ++++++++++++++++---------------------------- 1 file changed, 19 insertions(+), 33 deletions(-) diff --git a/lib/tree-view.coffee b/lib/tree-view.coffee index cd2e2669..9a85bb1b 100644 --- a/lib/tree-view.coffee +++ b/lib/tree-view.coffee @@ -718,9 +718,6 @@ class TreeView # Public: Paste a copied or cut item. # If a file is selected, the file's parent directory is used as the # paste destination. - # - # - # Returns `destination newPath`. pasteEntries: -> selectedEntry = @selectedEntry() cutPaths = if window.localStorage['tree-view:cutPath'] then JSON.parse(window.localStorage['tree-view:cutPath']) else null @@ -729,23 +726,14 @@ class TreeView for initialPath in initialPaths ? [] if selectedEntry and initialPath and fs.existsSync(initialPath) - basePath = selectedEntry.getPath() - basePath = path.dirname(basePath) if selectedEntry.classList.contains('file') - newPath = path.join(basePath, path.basename(initialPath)) + newDirectoryPath = selectedEntry.getPath() + newDirectoryPath = path.dirname(newDirectoryPath) if selectedEntry.classList.contains('file') + newPath = path.join(newDirectoryPath, path.basename(initialPath)) if copiedPaths - @copyEntry(initialPath, newPath) + @copyEntry(initialPath, newDirectoryPath) else if cutPaths - # Only move the target if the cut target doesn't exist and if the newPath - # is not within the initial path - unless fs.existsSync(newPath) or newPath.startsWith(initialPath) - try - @emitter.emit 'will-move-entry', {initialPath, newPath} - fs.moveSync(initialPath, newPath) - @emitter.emit 'entry-moved', {initialPath, newPath} - catch error - @emitter.emit 'move-entry-failed', {initialPath, newPath} - atom.notifications.addWarning("Unable to paste paths: #{initialPaths}", detail: error.message) + @moveEntry(initialPath, newDirectoryPath) add: (isCreatingFile) -> selectedEntry = @selectedEntry() ? @roots[0] @@ -826,7 +814,11 @@ class TreeView pageDown: -> @element.scrollTop += @element.offsetHeight - copyEntry: (initialPath, newPath) -> + # Copies an entry from `initialPath` to `newDirectoryPath` + # If the entry already exists in `newDirectoryPath`, a number is appended to the basename + copyEntry: (initialPath, newDirectoryPath) -> + newPath = path.join(newDirectoryPath, path.basename(initialPath)) + # append a number to the file if an item with the same name exists fileCounter = 0 originalNewPath = newPath @@ -847,6 +839,7 @@ class TreeView fs.copySync(initialPath, newPath) else # read the old file and write a new one at target location + # TODO: Replace with fs.copyFileSync fs.writeFileSync(newPath, fs.readFileSync(initialPath)) @emitter.emit 'entry-copied', {initialPath, newPath} @@ -856,18 +849,14 @@ class TreeView catch error @emitter.emit 'copy-entry-failed', {initialPath, newPath} - atom.notifications.addWarning("Failed to copy entry #{initialPath} to #{newPath}", detail: error.message) + atom.notifications.addWarning("Failed to copy entry #{initialPath} to #{newDirectoryPath}", detail: error.message) + # Moves an entry from `initialPath` to `newDirectoryPath` moveEntry: (initialPath, newDirectoryPath) -> - if initialPath is newDirectoryPath - return - - entryName = path.basename(initialPath) - newPath = path.join(newDirectoryPath, entryName) + newPath = path.join(newDirectoryPath, path.basename(initialPath)) try @emitter.emit 'will-move-entry', {initialPath, newPath} - fs.makeTreeSync(newDirectoryPath) unless fs.existsSync(newDirectoryPath) fs.moveSync(initialPath, newPath) @emitter.emit 'entry-moved', {initialPath, newPath} @@ -1114,21 +1103,18 @@ class TreeView # iterate backwards so files in a dir are moved before the dir itself for initialPath in initialPaths by -1 - continue if initialPath is newDirectoryPath @entryForPath(initialPath)?.collapse?() if (process.platform is 'darwin' and e.metaKey) or e.ctrlKey - @copyEntry(initialPath, path.join(newDirectoryPath, path.basename(initialPath))) + @copyEntry(initialPath, newDirectoryPath) else @moveEntry(initialPath, newDirectoryPath) else # Drop event from OS entry.classList.remove('selected') - if (process.platform is 'darwin' and e.metaKey) or e.ctrlKey - for file in e.dataTransfer.files - continue if file.path is newDirectoryPath - @copyEntry(file.path, path.join(newDirectoryPath, path.basename(file.path))) - else - for file in e.dataTransfer.files + for file in e.dataTransfer.files + if (process.platform is 'darwin' and e.metaKey) or e.ctrlKey + @copyEntry(file.path, newDirectoryPath) + else @moveEntry(file.path, newDirectoryPath) else if e.dataTransfer.files.length # Drop event from OS that isn't targeting a folder: add a new project folder From fdf8d777e895fe5cbb063d173f0a16dc2004c2f8 Mon Sep 17 00:00:00 2001 From: Wliu <50Wliu@users.noreply.github.com> Date: Sun, 1 Jul 2018 16:40:08 -0400 Subject: [PATCH 08/12] Add spec for copying in same directory --- spec/tree-view-package-spec.coffee | 44 +++++++++++++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) diff --git a/spec/tree-view-package-spec.coffee b/spec/tree-view-package-spec.coffee index bd6383dd..2ca50b4e 100644 --- a/spec/tree-view-package-spec.coffee +++ b/spec/tree-view-package-spec.coffee @@ -3668,6 +3668,18 @@ describe "TreeView", -> [alphaDirPath, betaFilePath, etaDirPath, gammaDirPath, deltaFilePath, epsilonFilePath, thetaFilePath] = [] beforeEach -> + # tree-view + # ├── alpha/ + # │   ├── beta.txt + # │   └── eta/ + # ├── alpha.txt + # ├── gamma/ + # │   ├── delta.txt + # │   ├── epsilon.txt + # │   └── theta/ + # │      └── theta.txt + # └── zeta.txt + rootDirPath = fs.absolute(temp.mkdirSync('tree-view')) alphaFilePath = path.join(rootDirPath, "alpha.txt") @@ -4030,7 +4042,7 @@ describe "TreeView", -> expect(editors[1].getPath()).toBe thetaFilePath2 describe "when dropping a DirectoryView and FileViews onto the same DirectoryView's header", -> - it "should not move the files and directory to the hovered directory", -> + it "should not move the files and directory", -> # Dragging alpha.txt and alphaDir into alphaDir alphaFile = treeView.roots[0].entries.children[2] alphaDir = findDirectoryContainingText(treeView.roots[0], 'alpha') @@ -4047,6 +4059,36 @@ describe "TreeView", -> treeView.onDrop(dropEvent) expect(treeView.moveEntry).not.toHaveBeenCalled() + describe "when dropping a DirectoryView and FileViews in the same parent DirectoryView", -> + describe "when the ctrl/cmd modifier key is pressed", -> + it "should copy the files and directory", -> + # Dragging beta.txt and etaDir into alphaDir + alphaDir = findDirectoryContainingText(treeView.roots[0], 'alpha') + alphaDir.expand() + betaFile = alphaDir.entries.children[0] + etaDir = alphaDir.entries.children[1] + + dragged = [betaFile, etaDir] + + alphaDirContents = alphaDir.querySelectorAll('.entry').length + + [dragStartEvent, dragEnterEvent, dropEvent] = + eventHelpers.buildInternalDragEvents(dragged, alphaDir.querySelector('.header'), alphaDir, treeView, true) + + spyOn(treeView, 'copyEntry').andCallThrough() + + treeView.onDragStart(dragStartEvent) + treeView.onDrop(dropEvent) + expect(treeView.copyEntry).toHaveBeenCalled() + + waitsFor "directory view contents to refresh", -> + findDirectoryContainingText(treeView.roots[0], 'alpha').querySelectorAll('.entry').length > alphaDirContents + + runs -> + expect(findDirectoryContainingText(treeView.roots[0], 'alpha').querySelectorAll('.entry').length).toBe alphaDirContents + 2 + expect(fs.existsSync(path.join(alphaDirPath, 'beta0.txt'))).toBe true + expect(fs.existsSync(path.join(alphaDirPath, 'eta0'))).toBe true + describe "when dragging a file from the OS onto a DirectoryView's header", -> it "should move the file to the hovered directory", -> # Dragging delta.txt from OS file explorer onto alphaDir From 4453d558b23f10981b5c64087a1745d2bfd2b874 Mon Sep 17 00:00:00 2001 From: Wliu <50Wliu@users.noreply.github.com> Date: Sun, 1 Jul 2018 16:51:48 -0400 Subject: [PATCH 09/12] :art: --- lib/tree-view.coffee | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/lib/tree-view.coffee b/lib/tree-view.coffee index 9a85bb1b..c44a27fe 100644 --- a/lib/tree-view.coffee +++ b/lib/tree-view.coffee @@ -720,16 +720,18 @@ class TreeView # paste destination. pasteEntries: -> selectedEntry = @selectedEntry() + return unless selectedEntry + cutPaths = if window.localStorage['tree-view:cutPath'] then JSON.parse(window.localStorage['tree-view:cutPath']) else null copiedPaths = if window.localStorage['tree-view:copyPath'] then JSON.parse(window.localStorage['tree-view:copyPath']) else null initialPaths = copiedPaths or cutPaths + return unless initialPaths?.length - for initialPath in initialPaths ? [] - if selectedEntry and initialPath and fs.existsSync(initialPath) - newDirectoryPath = selectedEntry.getPath() - newDirectoryPath = path.dirname(newDirectoryPath) if selectedEntry.classList.contains('file') - newPath = path.join(newDirectoryPath, path.basename(initialPath)) + newDirectoryPath = selectedEntry.getPath() + newDirectoryPath = path.dirname(newDirectoryPath) if selectedEntry.classList.contains('file') + for initialPath in initialPaths + if fs.existsSync(initialPath) if copiedPaths @copyEntry(initialPath, newDirectoryPath) else if cutPaths From d324665a482fd541b4789a5fb00768053de40132 Mon Sep 17 00:00:00 2001 From: Winston Liu <50Wliu@users.noreply.github.com> Date: Thu, 12 Jul 2018 17:02:41 -0400 Subject: [PATCH 10/12] Just test copy because it turns out the for loop wasn't working anyway --- spec/tree-view-package-spec.coffee | 121 ++++++++++++++--------------- 1 file changed, 60 insertions(+), 61 deletions(-) diff --git a/spec/tree-view-package-spec.coffee b/spec/tree-view-package-spec.coffee index 883e5788..186a3e57 100644 --- a/spec/tree-view-package-spec.coffee +++ b/spec/tree-view-package-spec.coffee @@ -1541,67 +1541,66 @@ describe "TreeView", -> LocalStorage.clear() atom.notifications.clear() - for operation in ['copy', 'cut'] - describe "when attempting to #{operation} and paste a directory into itself", -> - it "shows a warning notification and does not paste", -> - # /dir-1/ -> /dir-1/ - LocalStorage["tree-view:#{operation}Path"] = JSON.stringify([dirPath]) - newPath = path.join(dirPath, path.basename(dirPath)) - dirView.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) - expect(-> atom.commands.dispatch(treeView.element, "tree-view:paste")).not.toThrow() - expect(fs.existsSync(newPath)).toBe false - expect(atom.notifications.getNotifications()[0].getMessage()).toContain 'Cannot paste a folder into itself' - - describe "when attempting to #{operation} and paste a directory into a nested child directory", -> - it "shows a warning notification and does not paste", -> - nestedPath = path.join(dirPath, 'nested') - fs.makeTreeSync(nestedPath) - - # /dir-1/ -> /dir-1/nested/ - LocalStorage["tree-view:#{operation}Path"] = JSON.stringify([dirPath]) - newPath = path.join(nestedPath, path.basename(dirPath)) - dirView.reload() - nestedView = dirView.querySelector('.directory') - nestedView.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) - expect(-> atom.commands.dispatch(treeView.element, "tree-view:paste")).not.toThrow() - expect(fs.existsSync(newPath)).toBe false - expect(atom.notifications.getNotifications()[0].getMessage()).toContain 'Cannot paste a folder into itself' - - describe "when attempting to #{operation} and paste a directory into a sibling directory that starts with the same letter", -> - it "allows the paste to occur", -> - # /dir-1/ -> /dir-2/ - LocalStorage["tree-view:#{operation}Path"] = JSON.stringify([dirPath]) - newPath = path.join(dirPath2, path.basename(dirPath)) - dirView2.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) - expect(-> atom.commands.dispatch(treeView.element, "tree-view:paste")).not.toThrow() - expect(fs.existsSync(newPath)).toBe true - expect(atom.notifications.getNotifications()[0]).toBeUndefined() - - describe "when attempting to #{operation} and paste a directory into a symlink of itself", -> - it "shows a warning notification and does not paste", -> - fs.symlinkSync(dirPath, path.join(rootDirPath, 'symdir'), 'junction') - - # /dir-1/ -> symlink of /dir-1/ - LocalStorage["tree-view:#{operation}Path"] = JSON.stringify([dirPath]) - newPath = path.join(dirPath, path.basename(dirPath)) - symlinkView = root1.querySelector('.directory') - symlinkView.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) - expect(-> atom.commands.dispatch(treeView.element, "tree-view:paste")).not.toThrow() - expect(fs.existsSync(newPath)).toBe false - expect(atom.notifications.getNotifications()[0].getMessage()).toContain 'Cannot paste a folder into itself' - - describe "when attempting to #{operation} and paste a symlink into its target directory", -> - it "allows the paste to occur", -> - symlinkedPath = path.join(rootDirPath, 'symdir') - fs.symlinkSync(dirPath, symlinkedPath, 'junction') - - # symlink of /dir-1/ -> /dir-1/ - LocalStorage["tree-view:#{operation}Path"] = JSON.stringify([symlinkedPath]) - newPath = path.join(dirPath, path.basename(symlinkedPath)) - dirView.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) - expect(-> atom.commands.dispatch(treeView.element, "tree-view:paste")).not.toThrow() - expect(fs.existsSync(newPath)).toBe true - expect(atom.notifications.getNotifications()[0]).toBeUndefined() + describe "when attempting to paste a directory into itself", -> + it "shows a warning notification and does not paste", -> + # /dir-1/ -> /dir-1/ + LocalStorage["tree-view:copyPath"] = JSON.stringify([dirPath]) + newPath = path.join(dirPath, path.basename(dirPath)) + dirView.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) + expect(-> atom.commands.dispatch(treeView.element, "tree-view:paste")).not.toThrow() + expect(fs.existsSync(newPath)).toBe false + expect(atom.notifications.getNotifications()[0].getMessage()).toContain 'Cannot copy a folder into itself' + + describe "when attempting to paste a directory into a nested child directory", -> + it "shows a warning notification and does not paste", -> + nestedPath = path.join(dirPath, 'nested') + fs.makeTreeSync(nestedPath) + + # /dir-1/ -> /dir-1/nested/ + LocalStorage["tree-view:copyPath"] = JSON.stringify([dirPath]) + newPath = path.join(nestedPath, path.basename(dirPath)) + dirView.reload() + nestedView = dirView.querySelector('.directory') + nestedView.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) + expect(-> atom.commands.dispatch(treeView.element, "tree-view:paste")).not.toThrow() + expect(fs.existsSync(newPath)).toBe false + expect(atom.notifications.getNotifications()[0].getMessage()).toContain 'Cannot copy a folder into itself' + + describe "when attempting to paste a directory into a sibling directory that starts with the same letter", -> + it "allows the paste to occur", -> + # /dir-1/ -> /dir-2/ + LocalStorage["tree-view:copyPath"] = JSON.stringify([dirPath]) + newPath = path.join(dirPath2, path.basename(dirPath)) + dirView2.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) + expect(-> atom.commands.dispatch(treeView.element, "tree-view:paste")).not.toThrow() + expect(fs.existsSync(newPath)).toBe true + expect(atom.notifications.getNotifications()[0]).toBeUndefined() + + describe "when attempting to paste a directory into a symlink of itself", -> + it "shows a warning notification and does not paste", -> + fs.symlinkSync(dirPath, path.join(rootDirPath, 'symdir'), 'junction') + + # /dir-1/ -> symlink of /dir-1/ + LocalStorage["tree-view:copyPath"] = JSON.stringify([dirPath]) + newPath = path.join(dirPath, path.basename(dirPath)) + symlinkView = root1.querySelector('.directory') + symlinkView.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) + expect(-> atom.commands.dispatch(treeView.element, "tree-view:paste")).not.toThrow() + expect(fs.existsSync(newPath)).toBe false + expect(atom.notifications.getNotifications()[0].getMessage()).toContain 'Cannot copy a folder into itself' + + describe "when attempting to paste a symlink into its target directory", -> + it "allows the paste to occur", -> + symlinkedPath = path.join(rootDirPath, 'symdir') + fs.symlinkSync(dirPath, symlinkedPath, 'junction') + + # symlink of /dir-1/ -> /dir-1/ + LocalStorage["tree-view:copyPath"] = JSON.stringify([symlinkedPath]) + newPath = path.join(dirPath, path.basename(symlinkedPath)) + dirView.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) + expect(-> atom.commands.dispatch(treeView.element, "tree-view:paste")).not.toThrow() + expect(fs.existsSync(newPath)).toBe true + expect(atom.notifications.getNotifications()[0]).toBeUndefined() describe "when pasting entries which don't exist anymore", -> it "skips the entry which doesn't exist", -> From f7f82bc21d04137111a9a41f8f0413a396bf805f Mon Sep 17 00:00:00 2001 From: Winston Liu <50Wliu@users.noreply.github.com> Date: Thu, 21 Mar 2019 12:57:34 -0400 Subject: [PATCH 11/12] Update spec --- spec/tree-view-package-spec.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/tree-view-package-spec.coffee b/spec/tree-view-package-spec.coffee index 8211a06a..d2c24f06 100644 --- a/spec/tree-view-package-spec.coffee +++ b/spec/tree-view-package-spec.coffee @@ -1858,7 +1858,7 @@ describe "TreeView", -> expect(callback).not.toHaveBeenCalled() expect(atom.notifications.getNotifications().length).toBe(1) - expect(atom.notifications.getNotifications()[0].getMessage()).toContain('Unable to paste') + expect(atom.notifications.getNotifications()[0].getMessage()).toContain('Failed to move') describe 'when the file is currently open', -> beforeEach -> From 9dce9f23fdba781b13f4e2a234f0bb55b15a072a Mon Sep 17 00:00:00 2001 From: Winston Liu <50Wliu@users.noreply.github.com> Date: Tue, 14 May 2019 13:06:50 -0400 Subject: [PATCH 12/12] Redo tree-view:cut specs to take into account #1180 --- lib/tree-view.coffee | 2 +- spec/tree-view-package-spec.coffee | 116 +++++++++++++++++++++++++---- 2 files changed, 103 insertions(+), 15 deletions(-) diff --git a/lib/tree-view.coffee b/lib/tree-view.coffee index e86c17f7..a3bf2efb 100644 --- a/lib/tree-view.coffee +++ b/lib/tree-view.coffee @@ -707,7 +707,7 @@ class TreeView if copiedPaths @copyEntry(initialPath, newDirectoryPath) else if cutPaths - @moveEntry(initialPath, newDirectoryPath) + break unless @moveEntry(initialPath, newDirectoryPath) add: (isCreatingFile) -> selectedEntry = @selectedEntry() ? @roots[0] diff --git a/spec/tree-view-package-spec.coffee b/spec/tree-view-package-spec.coffee index a21c50ea..1ad8f4cd 100644 --- a/spec/tree-view-package-spec.coffee +++ b/spec/tree-view-package-spec.coffee @@ -1844,7 +1844,9 @@ describe "TreeView", -> expect(callback).toHaveBeenCalledWith({initialPath: filePath, newPath}) describe 'when the target destination file exists', -> - it 'emits a warning and does not move the cut file', -> + it "prompts to replace the file", -> + spyOn(atom, 'confirm') + callback = jasmine.createSpy("onEntryMoved") treeView.onEntryMoved(callback) @@ -1854,11 +1856,55 @@ describe "TreeView", -> fileView2.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) atom.commands.dispatch(treeView.element, "tree-view:paste") - expect(fs.existsSync(filePath)).toBeTruthy() - expect(callback).not.toHaveBeenCalled() + expect(atom.confirm).toHaveBeenCalled() + + describe "when selecting the replace option", -> + it "replaces the existing file", -> + spyOn(atom, 'confirm').andReturn 0 + + callback = jasmine.createSpy("onEntryMoved") + treeView.onEntryMoved(callback) + + filePath3 = path.join(dirPath2, "test-file.txt") + fs.writeFileSync(filePath3, "doesn't matter") + + fileView2.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) + atom.commands.dispatch(treeView.element, "tree-view:paste") + + expect(fs.existsSync(filePath)).toBe(false) + expect(callback).toHaveBeenCalledWith({initialPath: filePath, newPath: filePath3}) + + describe "when selecting the skip option", -> + it "does not replace the existing file", -> + spyOn(atom, 'confirm').andReturn 1 + + callback = jasmine.createSpy("onEntryMoved") + treeView.onEntryMoved(callback) + + filePath3 = path.join(dirPath2, "test-file.txt") + fs.writeFileSync(filePath3, "doesn't matter") + + fileView2.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) + atom.commands.dispatch(treeView.element, "tree-view:paste") + + expect(fs.existsSync(filePath)).toBe(true) + expect(callback).not.toHaveBeenCalled() + + describe "when cancelling the dialog", -> + it "does not replace the existing file", -> + spyOn(atom, 'confirm').andReturn 2 + + callback = jasmine.createSpy("onEntryMoved") + treeView.onEntryMoved(callback) - expect(atom.notifications.getNotifications().length).toBe(1) - expect(atom.notifications.getNotifications()[0].getMessage()).toContain('Failed to move') + filePath3 = path.join(dirPath2, "test-file.txt") + fs.writeFileSync(filePath3, "doesn't matter") + + fileView2.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) + atom.commands.dispatch(treeView.element, "tree-view:paste") + + expect(fs.existsSync(filePath)).toBe(true) + expect(callback).not.toHaveBeenCalled() describe 'when the file is currently open', -> beforeEach -> @@ -1930,20 +1976,62 @@ describe "TreeView", -> expect(callback).toHaveBeenCalledWith({initialPath: filePath2, newPath: newPath2}) expect(callback).toHaveBeenCalledWith({initialPath: filePath3, newPath: newPath3}) - describe 'when the target destination file exists', -> - it 'does not move the cut file', -> - LocalStorage['tree-view:cutPath'] = JSON.stringify([filePath2, filePath3]) + describe "when the target destination file exists", -> + filePath5 = null - filePath4 = path.join(dirPath, "test-file2.txt") - filePath5 = path.join(dirPath, "test-file3.txt") - fs.writeFileSync(filePath4, "doesn't matter") - fs.writeFileSync(filePath5, "doesn't matter") + beforeEach -> + filePath5 = path.join(dirPath2, "test-file.txt") # So that dirPath2 has an exact copy of files in dirPath + filePath6 = path.join(dirPath, "test-file2.txt") + filePath7 = path.join(dirPath, "test-file3.txt") + fs.writeFileSync(filePath5, "doesn't matter 5") + fs.writeFileSync(filePath6, "doesn't matter 6") + fs.writeFileSync(filePath7, "doesn't matter 7") + + LocalStorage['tree-view:cutPath'] = JSON.stringify([filePath5, filePath2, filePath3]) + + it "prompts for each file as long as cancel is not chosen", -> + calls = 0 + getButton = -> + calls++ + switch calls + when 1 + return 0 + when 2 + return 1 + when 3 + return 0 + + spyOn(atom, 'confirm').andCallFake -> getButton() fileView.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) atom.commands.dispatch(treeView.element, "tree-view:paste") - expect(fs.existsSync(filePath2)).toBeTruthy() - expect(fs.existsSync(filePath3)).toBeTruthy() + expect(atom.confirm.calls.length).toBe(3) + + expect(fs.existsSync(filePath5)).toBe(false) + expect(fs.existsSync(filePath2)).toBe(true) + expect(fs.existsSync(filePath3)).toBe(false) + + it "immediately cancels any pending file moves when cancel is chosen", -> + calls = 0 + getButton = -> + calls++ + switch calls + when 1 + return 0 + when 2 + return 2 + + spyOn(atom, 'confirm').andCallFake -> getButton() + + fileView.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) + atom.commands.dispatch(treeView.element, "tree-view:paste") + + expect(atom.confirm.calls.length).toBe(2) + + expect(fs.existsSync(filePath5)).toBe(false) + expect(fs.existsSync(filePath2)).toBe(true) + expect(fs.existsSync(filePath3)).toBe(true) describe "when a directory is selected", -> it "creates a copy of the original file in the selected directory and removes the original", ->