From fc6f2576ff4bd78149443af15d23908e3a016c5b Mon Sep 17 00:00:00 2001 From: Amin Yahyaabadi Date: Mon, 28 Dec 2020 00:07:10 -0600 Subject: [PATCH 01/14] feat: Add a command to delete items permanently --- keymaps/tree-view.cson | 1 + lib/tree-view-package.js | 3 ++- lib/tree-view.coffee | 8 ++++---- menus/tree-view.cson | 3 +++ 4 files changed, 10 insertions(+), 5 deletions(-) diff --git a/keymaps/tree-view.cson b/keymaps/tree-view.cson index 98828de7..00f24cf1 100644 --- a/keymaps/tree-view.cson +++ b/keymaps/tree-view.cson @@ -76,6 +76,7 @@ 'shift-a': 'tree-view:add-folder' 'd': 'tree-view:duplicate' 'delete': 'tree-view:remove' + 'shift-delete': 'tree-view:remove-permanently' 'backspace': 'tree-view:remove' 'k': 'core:move-up' 'j': 'core:move-down' diff --git a/lib/tree-view-package.js b/lib/tree-view-package.js index f624e03d..af7ba343 100644 --- a/lib/tree-view-package.js +++ b/lib/tree-view-package.js @@ -15,7 +15,8 @@ class TreeViewPackage { 'tree-view:add-file': () => this.getTreeViewInstance().add(true), 'tree-view:add-folder': () => this.getTreeViewInstance().add(false), 'tree-view:duplicate': () => this.getTreeViewInstance().copySelectedEntry(), - 'tree-view:remove': () => this.getTreeViewInstance().removeSelectedEntries(), + 'tree-view:remove': () => this.getTreeViewInstance().removeSelectedEntries(false), + 'tree-view:remove-permanently': () => this.getTreeViewInstance().removeSelectedEntries(true), 'tree-view:rename': () => this.getTreeViewInstance().moveSelectedEntry(), 'tree-view:show-current-file-in-file-manager': () => this.getTreeViewInstance().showCurrentFileInFileManager() })) diff --git a/lib/tree-view.coffee b/lib/tree-view.coffee index 61795fa3..e3198195 100644 --- a/lib/tree-view.coffee +++ b/lib/tree-view.coffee @@ -612,7 +612,7 @@ class TreeView @emitter.emit 'entry-copied', {initialPath, newPath} dialog.attach() - removeSelectedEntries: -> + removeSelectedEntries: (shouldDeletePermanently = false) -> if @hasFocus() selectedPaths = @selectedPaths() selectedEntries = @getSelectedEntries() @@ -632,9 +632,9 @@ class TreeView return atom.confirm({ - message: "Are you sure you want to delete the selected #{if selectedPaths.length > 1 then 'items' else 'item'}?", + message: "Are you sure you want to #{if shouldDeletePermanently then 'permanently ' else ''}delete the selected #{if selectedPaths.length > 1 then 'items' else 'item'}?", detailedMessage: "You are deleting:\n#{selectedPaths.join('\n')}", - buttons: ['Move to Trash', 'Cancel'] + buttons: [(if shouldDeletePermanently then 'Permanently Delete ⚠️' else 'Move to Trash'), 'Cancel'] }, (response) => if response is 0 # Move to Trash failedDeletions = [] @@ -646,7 +646,7 @@ class TreeView continue unless fs.existsSync(selectedPath) @emitter.emit 'will-delete-entry', {pathToDelete: selectedPath} - if shell.moveItemToTrash(selectedPath) + if shell.moveItemToTrash(selectedPath, shouldDeletePermanently) @emitter.emit 'entry-deleted', {pathToDelete: selectedPath} else @emitter.emit 'delete-entry-failed', {pathToDelete: selectedPath} diff --git a/menus/tree-view.cson b/menus/tree-view.cson index f108645a..fd7d689b 100644 --- a/menus/tree-view.cson +++ b/menus/tree-view.cson @@ -28,6 +28,7 @@ {'label': 'Rename', 'command': 'tree-view:move'} {'label': 'Duplicate', 'command': 'tree-view:duplicate'} {'label': 'Delete', 'command': 'tree-view:remove'} + {'label': 'Delete Permanently', 'command': 'tree-view:remove-permanently'} {'label': 'Copy', 'command': 'tree-view:copy'} {'label': 'Cut', 'command': 'tree-view:cut'} {'label': 'Paste', 'command': 'tree-view:paste'} @@ -57,6 +58,7 @@ {'label': 'Rename', 'command': 'tree-view:move'} {'label': 'Duplicate', 'command': 'tree-view:duplicate'} {'label': 'Delete', 'command': 'tree-view:remove'} + {'label': 'Delete Permanently', 'command': 'tree-view:remove-permanently'} {'label': 'Copy', 'command': 'tree-view:copy'} {'label': 'Cut', 'command': 'tree-view:cut'} {'label': 'Paste', 'command': 'tree-view:paste'} @@ -86,6 +88,7 @@ '.tree-view .multi-select': [ {'label': 'Delete', 'command': 'tree-view:remove'} + {'label': 'Delete Permanently', 'command': 'tree-view:remove-permanently'} {'label': 'Copy', 'command': 'tree-view:copy'} {'label': 'Cut', 'command': 'tree-view:cut'} {'label': 'Paste', 'command': 'tree-view:paste'} From e508a0123d84d9b0652dabd0509b8c60775c5dab Mon Sep 17 00:00:00 2001 From: Amin Yahyaabadi Date: Mon, 28 Dec 2020 02:04:30 -0600 Subject: [PATCH 02/14] refactor: finishRemoval This will be used in the next commit --- lib/tree-view.coffee | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/lib/tree-view.coffee b/lib/tree-view.coffee index e3198195..ac5da6a5 100644 --- a/lib/tree-view.coffee +++ b/lib/tree-view.coffee @@ -661,10 +661,7 @@ class TreeView detail: "#{failedDeletions.join('\n')}" dismissable: true - # Focus the first parent folder - if firstSelectedEntry = selectedEntries[0] - @selectEntry(firstSelectedEntry.closest('.directory:not(.selected)')) - @updateRoots() if atom.config.get('tree-view.squashDirectoryNames') + finishRemoval(selectedEntries[0]) ) formatTrashFailureMessage: (failedDeletions) -> @@ -678,6 +675,12 @@ class TreeView when 'darwin' then 'Is Trash enabled on the volume where the files are stored?' when 'win32' then 'Is there a Recycle Bin on the drive where the files are stored?' + finishRemoval: (firstSelectedEntry) -> + # Focus the first parent folder + if firstSelectedEntry + @selectEntry(firstSelectedEntry.closest('.directory:not(.selected)')) + @updateRoots() if atom.config.get('tree-view.squashDirectoryNames') + # Public: Copy the path of the selected entry element. # Save the path in localStorage, so that copying from 2 different # instances of atom works as intended From a51d9980522864fe624984026eb5074c75ab9244 Mon Sep 17 00:00:00 2001 From: Amin Yahyaabadi Date: Mon, 28 Dec 2020 02:56:44 -0600 Subject: [PATCH 03/14] feat: use del to remove the files as a fallback when shouldDeletePermanently --- lib/tree-view.coffee | 34 +++++-- package-lock.json | 233 ++++++++++++++++++++++++++++++++++++++++++- package.json | 1 + 3 files changed, 258 insertions(+), 10 deletions(-) diff --git a/lib/tree-view.coffee b/lib/tree-view.coffee index ac5da6a5..1bc632f7 100644 --- a/lib/tree-view.coffee +++ b/lib/tree-view.coffee @@ -5,6 +5,7 @@ _ = require 'underscore-plus' {BufferedProcess, CompositeDisposable, Emitter} = require 'atom' {repoForPath, getStyleObject, getFullExtension} = require "./helpers" fs = require 'fs-plus' +del = require 'del' AddDialog = require './add-dialog' MoveDialog = require './move-dialog' @@ -649,25 +650,42 @@ class TreeView if shell.moveItemToTrash(selectedPath, shouldDeletePermanently) @emitter.emit 'entry-deleted', {pathToDelete: selectedPath} else - @emitter.emit 'delete-entry-failed', {pathToDelete: selectedPath} + if not shouldDeletePermanently + @emitter.emit 'delete-entry-failed', {pathToDelete: selectedPath} failedDeletions.push selectedPath if repo = repoForPath(selectedPath) repo.getPathStatus(selectedPath) if failedDeletions.length > 0 - atom.notifications.addError @formatTrashFailureMessage(failedDeletions), - description: @formatTrashEnabledMessage() - detail: "#{failedDeletions.join('\n')}" - dismissable: true + if shouldDeletePermanently + del(selectedPaths, {force: true}) + .then( (deletedPaths) -> + for deletedPath in deletedPaths + @emitter.emit 'entry-deleted', {pathToDelete: deletedPath} + ) + .catch((err) -> + atom.notifications.addError @formatTrashFailureMessage(failedDeletions, true), + description: err + dismissable: true + for selectedPath in selectedPaths + @emitter.emit 'delete-entry-failed', {pathToDelete: selectedPath} + ) + .finally( -> @finishRemoval(selectedEntries[0])) + else + atom.notifications.addError @formatTrashFailureMessage(failedDeletions, false), + description: @formatTrashEnabledMessage() + detail: "#{failedDeletions.join('\n')}" + dismissable: true - finishRemoval(selectedEntries[0]) + if not shouldDeletePermanently + @finishRemoval(selectedEntries[0]) ) - formatTrashFailureMessage: (failedDeletions) -> + formatTrashFailureMessage: (failedDeletions, shouldDeletePermanently = false) -> fileText = if failedDeletions.length > 1 then 'files' else 'file' - "The following #{fileText} couldn't be moved to the trash." + "The following #{fileText} couldn't be #{if shouldDeletePermanently then "deleted permanently" else "moved to the trash."}" formatTrashEnabledMessage: -> switch process.platform diff --git a/package-lock.json b/package-lock.json index d070ae7c..88653799 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4,6 +4,29 @@ "lockfileVersion": 1, "requires": true, "dependencies": { + "@nodelib/fs.scandir": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.4.tgz", + "integrity": "sha512-33g3pMJk3bg5nXbL/+CY6I2eJDzZAni49PfJnL5fghPTggPvBd/pFNSgJsdAgWptuFu7qq/ERvOYFlhvsLTCKA==", + "requires": { + "@nodelib/fs.stat": "2.0.4", + "run-parallel": "^1.1.9" + } + }, + "@nodelib/fs.stat": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.4.tgz", + "integrity": "sha512-IYlHJA0clt2+Vg7bccq+TzRdJvv19c2INqBSsoOLp1je7xjtr7J26+WXR72MCdvU9q1qTzIWDfhMf+DRvQJK4Q==" + }, + "@nodelib/fs.walk": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.6.tgz", + "integrity": "sha512-8Broas6vTtW4GIXTAHDoE32hnN2M5ykgCpWGbuXHQ15vEMqr23pB76e/GZcYsZCHALv50ktd24qhEyKr6wBtow==", + "requires": { + "@nodelib/fs.scandir": "2.1.4", + "fastq": "^1.6.0" + } + }, "acorn": { "version": "5.7.3", "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.3.tgz", @@ -27,6 +50,15 @@ } } }, + "aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "requires": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + } + }, "ajv": { "version": "5.5.2", "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", @@ -82,6 +114,11 @@ "es-abstract": "^1.7.0" } }, + "array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==" + }, "async": { "version": "1.5.2", "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", @@ -136,6 +173,14 @@ "concat-map": "0.0.1" } }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "requires": { + "fill-range": "^7.0.1" + } + }, "buffer-from": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", @@ -206,6 +251,11 @@ "integrity": "sha512-UZK3NBx2Mca+b5LsG7bY183pHWt5Y1xts4P3Pz7ENTwGVnJOUWbRb3ocjvX7hx9tq/yTAdclXm9sZ38gNuem4A==", "dev": true }, + "clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==" + }, "cli-cursor": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", @@ -366,6 +416,51 @@ "uniq": "^1.0.1" } }, + "del": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/del/-/del-6.0.0.tgz", + "integrity": "sha512-1shh9DQ23L16oXSZKB2JxpL7iMy2E0S9d517ptA1P8iw0alkPtQcrKH7ru31rYtKwF499HkTu+DRzq3TCKDFRQ==", + "requires": { + "globby": "^11.0.1", + "graceful-fs": "^4.2.4", + "is-glob": "^4.0.1", + "is-path-cwd": "^2.2.0", + "is-path-inside": "^3.0.2", + "p-map": "^4.0.0", + "rimraf": "^3.0.2", + "slash": "^3.0.0" + }, + "dependencies": { + "graceful-fs": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", + "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==" + }, + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "requires": { + "glob": "^7.1.3" + } + } + } + }, + "dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "requires": { + "path-type": "^4.0.0" + }, + "dependencies": { + "path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==" + } + } + }, "doctrine": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", @@ -842,6 +937,19 @@ "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=", "dev": true }, + "fast-glob": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.4.tgz", + "integrity": "sha512-kr/Oo6PX51265qeuCYsyGypiO5uJFgBS0jksyG7FUeCyQzNwYnzrNIMR1NXfkZXsMYXYLRAHgISHBz8gQcxKHQ==", + "requires": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.0", + "merge2": "^1.3.0", + "micromatch": "^4.0.2", + "picomatch": "^2.2.1" + } + }, "fast-json-stable-stringify": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", @@ -854,6 +962,14 @@ "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", "dev": true }, + "fastq": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.10.0.tgz", + "integrity": "sha512-NL2Qc5L3iQEsyYzweq7qfgy5OtXCmGzGvhElGEd/SoFWEMOEczNh5s5ocaF01HDetxz+p8ecjNPA6cZxxIHmzA==", + "requires": { + "reusify": "^1.0.4" + } + }, "figures": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", @@ -873,6 +989,14 @@ "object-assign": "^4.0.1" } }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "requires": { + "to-regex-range": "^5.0.1" + } + }, "find-root": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", @@ -957,12 +1081,40 @@ } } }, + "glob-parent": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", + "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==", + "requires": { + "is-glob": "^4.0.1" + } + }, "globals": { "version": "11.10.0", "resolved": "https://registry.npmjs.org/globals/-/globals-11.10.0.tgz", "integrity": "sha512-0GZF1RiPKU97IHUO5TORo9w1PwrH/NBPl+fS7oMLdaTRiYmYbwK4NWoZWrAdd0/abG9R2BU+OiwyQpTpE6pdfQ==", "dev": true }, + "globby": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.1.tgz", + "integrity": "sha512-iH9RmgwCmUJHi2z5o2l3eTtGBtXek1OYlHrbcxOYugyHLmAsZrPj43OtHThd62Buh/Vv6VyCBD2bdyWcGNQqoQ==", + "requires": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.1.1", + "ignore": "^5.1.4", + "merge2": "^1.3.0", + "slash": "^3.0.0" + }, + "dependencies": { + "ignore": { + "version": "5.1.8", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", + "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==" + } + } + }, "graceful-fs": { "version": "4.1.15", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz", @@ -1033,6 +1185,11 @@ "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", "dev": true }, + "indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==" + }, "inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -1096,12 +1253,40 @@ "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=", "dev": true }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=" + }, "is-fullwidth-code-point": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", "dev": true }, + "is-glob": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", + "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==" + }, + "is-path-cwd": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz", + "integrity": "sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==" + }, + "is-path-inside": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.2.tgz", + "integrity": "sha512-/2UGPSgmtqwo1ktx8NDHjuPwZWmHhO+gj0f93EkhLB5RgW9RZevWYYlIkS6zePc6U2WpOdQYIwHe9YC4DWEBVg==" + }, "is-promise": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", @@ -1239,6 +1424,20 @@ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.7.3.tgz", "integrity": "sha1-bUUk6LlV+V1PW1iFHOId1y+06VI=" }, + "merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==" + }, + "micromatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", + "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", + "requires": { + "braces": "^3.0.1", + "picomatch": "^2.0.5" + } + }, "mimic-fn": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", @@ -1397,6 +1596,14 @@ "p-limit": "^1.1.0" } }, + "p-map": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "requires": { + "aggregate-error": "^3.0.0" + } + }, "p-try": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", @@ -1466,6 +1673,11 @@ } } }, + "picomatch": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", + "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==" + }, "pify": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", @@ -1649,6 +1861,11 @@ "signal-exit": "^3.0.2" } }, + "reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==" + }, "rimraf": { "version": "2.6.3", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", @@ -1669,8 +1886,7 @@ "run-parallel": { "version": "1.1.9", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.1.9.tgz", - "integrity": "sha512-DEqnSRTDw/Tc3FXf49zedI638Z9onwUotBMiUFKmrO2sdFKIbXamXGQ3Axd4qgphxKB4kw/qP1w5kTxnfU1B9Q==", - "dev": true + "integrity": "sha512-DEqnSRTDw/Tc3FXf49zedI638Z9onwUotBMiUFKmrO2sdFKIbXamXGQ3Axd4qgphxKB4kw/qP1w5kTxnfU1B9Q==" }, "rx-lite": { "version": "4.0.8", @@ -1730,6 +1946,11 @@ "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", "dev": true }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==" + }, "slice-ansi": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-1.0.0.tgz", @@ -1911,6 +2132,14 @@ "os-tmpdir": "~1.0.2" } }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "requires": { + "is-number": "^7.0.0" + } + }, "type": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", diff --git a/package.json b/package.json index b82c2be9..093c91cd 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ }, "private": true, "dependencies": { + "del": "^6.0.0", "fs-plus": "^3.0.0", "minimatch": "~0.3.0", "pathwatcher": "^8.1.0", From 37ee0cdb891c460033f603b07f95fe9dae5bf0e6 Mon Sep 17 00:00:00 2001 From: Amin Yahyaabadi Date: Mon, 28 Dec 2020 04:09:52 -0600 Subject: [PATCH 04/14] refactor: removeSelectedEntriesPermanently --- lib/tree-view.coffee | 48 ++++++++++++++++++++++---------------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/lib/tree-view.coffee b/lib/tree-view.coffee index 1bc632f7..25325306 100644 --- a/lib/tree-view.coffee +++ b/lib/tree-view.coffee @@ -638,6 +638,8 @@ class TreeView buttons: [(if shouldDeletePermanently then 'Permanently Delete ⚠️' else 'Move to Trash'), 'Cancel'] }, (response) => if response is 0 # Move to Trash + if shouldDeletePermanently + return @removeSelectedPathsPermanently(selectedPaths, selectedEntries) failedDeletions = [] for selectedPath in selectedPaths # Don't delete entries which no longer exist. This can happen, for example, when: @@ -647,39 +649,22 @@ class TreeView continue unless fs.existsSync(selectedPath) @emitter.emit 'will-delete-entry', {pathToDelete: selectedPath} - if shell.moveItemToTrash(selectedPath, shouldDeletePermanently) + if shell.moveItemToTrash(selectedPath) @emitter.emit 'entry-deleted', {pathToDelete: selectedPath} else - if not shouldDeletePermanently - @emitter.emit 'delete-entry-failed', {pathToDelete: selectedPath} + @emitter.emit 'delete-entry-failed', {pathToDelete: selectedPath} failedDeletions.push selectedPath if repo = repoForPath(selectedPath) repo.getPathStatus(selectedPath) if failedDeletions.length > 0 - if shouldDeletePermanently - del(selectedPaths, {force: true}) - .then( (deletedPaths) -> - for deletedPath in deletedPaths - @emitter.emit 'entry-deleted', {pathToDelete: deletedPath} - ) - .catch((err) -> - atom.notifications.addError @formatTrashFailureMessage(failedDeletions, true), - description: err - dismissable: true - for selectedPath in selectedPaths - @emitter.emit 'delete-entry-failed', {pathToDelete: selectedPath} - ) - .finally( -> @finishRemoval(selectedEntries[0])) - else - atom.notifications.addError @formatTrashFailureMessage(failedDeletions, false), - description: @formatTrashEnabledMessage() - detail: "#{failedDeletions.join('\n')}" - dismissable: true + atom.notifications.addError @formatTrashFailureMessage(failedDeletions, false), + description: @formatTrashEnabledMessage() + detail: "#{failedDeletions.join('\n')}" + dismissable: true - if not shouldDeletePermanently - @finishRemoval(selectedEntries[0]) + @finishRemoval(selectedEntries[0]) ) formatTrashFailureMessage: (failedDeletions, shouldDeletePermanently = false) -> @@ -699,6 +684,21 @@ class TreeView @selectEntry(firstSelectedEntry.closest('.directory:not(.selected)')) @updateRoots() if atom.config.get('tree-view.squashDirectoryNames') + removeSelectedPathsPermanently: (selectedPaths, selectedEntries) -> + del(selectedPaths, {force: true}) + .then( (deletedPaths) -> + for deletedPath in deletedPaths + @emitter.emit 'entry-deleted', {pathToDelete: deletedPath} + ) + .catch((err) -> + atom.notifications.addError @formatTrashFailureMessage(selectedPaths, true), + description: err + dismissable: true + for selectedPath in selectedPaths + @emitter.emit 'delete-entry-failed', {pathToDelete: selectedPath} + ) + .finally( -> @finishRemoval(selectedEntries[0])) + # Public: Copy the path of the selected entry element. # Save the path in localStorage, so that copying from 2 different # instances of atom works as intended From e3a5650a281a35bbf4fcdd208cd2aee17a16d231 Mon Sep 17 00:00:00 2001 From: Amin Yahyaabadi Date: Mon, 28 Dec 2020 04:10:04 -0600 Subject: [PATCH 05/14] ci: use node 12 on windows --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index 8801256c..540410af 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -20,7 +20,7 @@ environment: - ATOM_CHANNEL: beta install: - - ps: Install-Product node 6 + - ps: Install-Product node 12 build_script: - ps: iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/atom/ci/master/build-package.ps1')) From 1ad243463928c8a5c4f51fa3c4ce6cf49dd00ab5 Mon Sep 17 00:00:00 2001 From: Amin Yahyaabadi Date: Thu, 8 Apr 2021 15:30:40 -0500 Subject: [PATCH 06/14] fix: call will-delete-entry before permanently deleting --- lib/tree-view.coffee | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/tree-view.coffee b/lib/tree-view.coffee index 25325306..3a496849 100644 --- a/lib/tree-view.coffee +++ b/lib/tree-view.coffee @@ -685,6 +685,8 @@ class TreeView @updateRoots() if atom.config.get('tree-view.squashDirectoryNames') removeSelectedPathsPermanently: (selectedPaths, selectedEntries) -> + for selectedPath in selectedPaths + @emitter.emit 'will-delete-entry', {pathToDelete: selectedPath} del(selectedPaths, {force: true}) .then( (deletedPaths) -> for deletedPath in deletedPaths From a23facda69a5fad10b3f5ce107f4a091becd8fa3 Mon Sep 17 00:00:00 2001 From: Amin Yahyaabadi Date: Thu, 8 Apr 2021 15:49:26 -0500 Subject: [PATCH 07/14] return del promise --- lib/tree-view.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/tree-view.coffee b/lib/tree-view.coffee index 3a496849..fe2c8d1c 100644 --- a/lib/tree-view.coffee +++ b/lib/tree-view.coffee @@ -687,7 +687,7 @@ class TreeView removeSelectedPathsPermanently: (selectedPaths, selectedEntries) -> for selectedPath in selectedPaths @emitter.emit 'will-delete-entry', {pathToDelete: selectedPath} - del(selectedPaths, {force: true}) + return del(selectedPaths, {force: true}) .then( (deletedPaths) -> for deletedPath in deletedPaths @emitter.emit 'entry-deleted', {pathToDelete: deletedPath} From c86858928ad7e85a02c4f4e1553d0e049b66eb81 Mon Sep 17 00:00:00 2001 From: Amin Yahyaabadi Date: Thu, 8 Apr 2021 20:21:08 -0500 Subject: [PATCH 08/14] test: add tests for remove-permanently --- spec/async-helper.js | 32 +++++++++++++++++++ spec/tree-view-package-spec.coffee | 49 ++++++++++++++++++++++++++++++ 2 files changed, 81 insertions(+) create mode 100644 spec/async-helper.js diff --git a/spec/async-helper.js b/spec/async-helper.js new file mode 100644 index 00000000..b0b8f21b --- /dev/null +++ b/spec/async-helper.js @@ -0,0 +1,32 @@ +/** + * Spy on an async method and call through + * @param obj {Record | any} + * @param method {string} + * @returns {Jasmine.Spy & {resolvedWith: any, calledWith: Array}} + */ +function spyOnAsyncAndCallThrough (obj, method) { + const originalMethod = obj[method] + if (typeof originalMethod !== 'function') { + throw new Error(`${method} is not a method of ${obj}`) + } + let resolvedWith + let calledWith + let asyncSpy = spyOn(obj, method) + .andCallFake((...args) => { + calledWith = args + originalMethod(...args) + .then((returnValue) => { + resolvedWith = returnValue + // update spy call information + asyncSpy.resolvedWith = resolvedWith + asyncSpy.calledWith = calledWith + }).catch((err) => { + throw err + }) + }) + // initial undefined values + asyncSpy.resolvedWith = resolvedWith + asyncSpy.calledWith = calledWith + return asyncSpy +} +exports.spyOnAsyncAndCallThrough = spyOnAsyncAndCallThrough diff --git a/spec/tree-view-package-spec.coffee b/spec/tree-view-package-spec.coffee index 85a2233d..24bb0cdf 100644 --- a/spec/tree-view-package-spec.coffee +++ b/spec/tree-view-package-spec.coffee @@ -6,6 +6,7 @@ os = require 'os' {remote, shell} = require 'electron' Directory = require '../lib/directory' eventHelpers = require "./event-helpers" +spyOnAsyncAndCallThrough = require('./async-helper').spyOnAsyncAndCallThrough isCaseSensitive = null isFilesystemCaseSensitive = -> @@ -3133,6 +3134,54 @@ describe "TreeView", -> runs -> expect(atom.notifications.getNotifications().length).toBe 0 + describe "treev-view:remove-permanently", -> + beforeEach -> + jasmine.attachToDOM(workspaceElement) + + it "won't remove the root directory", -> + spyOn(atom, 'confirm') + treeView.focus() + root1.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) + atom.commands.dispatch(treeView.element, 'tree-view:remove-permanently') + + args = atom.confirm.mostRecentCall.args[0] + expect(args.buttons).toEqual ['OK'] + + it "shows the native alert dialog", -> + spyOn(atom, 'confirm') + + waitForWorkspaceOpenEvent -> + fileView.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1})) + + runs -> + atom.commands.dispatch(treeView.element, 'tree-view:remove-permanently') + args = atom.confirm.mostRecentCall.args[0] + expect(args.buttons).toEqual ['Permanently Delete ⚠️', 'Cancel'] + + + it "calls removeSelectedEntries and removeSelectedPathsPermanently", -> + spyOn(atom, 'confirm') + + removeSelectedPathsPermanentlySpy = spyOnAsyncAndCallThrough(treeView, 'removeSelectedPathsPermanently') + removeSelectedEntriesSpy = spyOn(treeView, 'removeSelectedEntries').andCallThrough() + + filePath = path.join(os.tmpdir(), 'non-project-file.txt') + fs.writeFileSync(filePath, 'test') + + waitsForPromise -> + atom.workspace.open(filePath) + + waitsForPromise -> + atom.commands.dispatch(treeView.element, 'tree-view:remove-permanently') + + waitsFor -> + removeSelectedPathsPermanentlySpy.calledWith isnt undefined + + waitsFor 'removeSelectedEntries amd removeSelectedPathsPermanently to be called', -> + removeSelectedEntriesSpy.callCount is 1 and + removeSelectedEntriesSpy.mostRecentCall.args[0] is true and + removeSelectedPathsPermanentlySpy.calledWith[0] is [filePath] + describe "file system events", -> temporaryFilePath = null From 7f2decd222e6632f690b84af2a7e901c9cb58d75 Mon Sep 17 00:00:00 2001 From: Amin Yahyaabadi Date: Thu, 8 Apr 2021 20:31:20 -0500 Subject: [PATCH 09/14] test: andCallFake atom.confirm --- 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 24bb0cdf..f6249d70 100644 --- a/spec/tree-view-package-spec.coffee +++ b/spec/tree-view-package-spec.coffee @@ -3160,7 +3160,7 @@ describe "TreeView", -> it "calls removeSelectedEntries and removeSelectedPathsPermanently", -> - spyOn(atom, 'confirm') + spyOn(atom, 'confirm').andCallFake (options, callback) -> callback(0) removeSelectedPathsPermanentlySpy = spyOnAsyncAndCallThrough(treeView, 'removeSelectedPathsPermanently') removeSelectedEntriesSpy = spyOn(treeView, 'removeSelectedEntries').andCallThrough() From 28bba00ef9535da25117990f37ca9d5811f4ddef Mon Sep 17 00:00:00 2001 From: Amin Yahyaabadi Date: Thu, 8 Apr 2021 20:33:07 -0500 Subject: [PATCH 10/14] test: use call to make sure this is defined --- spec/async-helper.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/async-helper.js b/spec/async-helper.js index b0b8f21b..7d0b23a9 100644 --- a/spec/async-helper.js +++ b/spec/async-helper.js @@ -1,6 +1,6 @@ /** * Spy on an async method and call through - * @param obj {Record | any} + * @param obj {Record} * @param method {string} * @returns {Jasmine.Spy & {resolvedWith: any, calledWith: Array}} */ @@ -14,7 +14,7 @@ function spyOnAsyncAndCallThrough (obj, method) { let asyncSpy = spyOn(obj, method) .andCallFake((...args) => { calledWith = args - originalMethod(...args) + originalMethod.call(obj, ...args) .then((returnValue) => { resolvedWith = returnValue // update spy call information From c8b942b14f2c23d4cd61f7b62dcb85d9eb4ed12e Mon Sep 17 00:00:00 2001 From: Amin Yahyaabadi Date: Thu, 8 Apr 2021 20:45:11 -0500 Subject: [PATCH 11/14] fix: use fat arrows to make sure this is defined --- lib/tree-view.coffee | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/tree-view.coffee b/lib/tree-view.coffee index fe2c8d1c..dafdc672 100644 --- a/lib/tree-view.coffee +++ b/lib/tree-view.coffee @@ -688,18 +688,18 @@ class TreeView for selectedPath in selectedPaths @emitter.emit 'will-delete-entry', {pathToDelete: selectedPath} return del(selectedPaths, {force: true}) - .then( (deletedPaths) -> + .then( (deletedPaths) => for deletedPath in deletedPaths @emitter.emit 'entry-deleted', {pathToDelete: deletedPath} ) - .catch((err) -> + .catch((err) => atom.notifications.addError @formatTrashFailureMessage(selectedPaths, true), description: err dismissable: true for selectedPath in selectedPaths @emitter.emit 'delete-entry-failed', {pathToDelete: selectedPath} ) - .finally( -> @finishRemoval(selectedEntries[0])) + .finally( => @finishRemoval(selectedEntries[0])) # Public: Copy the path of the selected entry element. # Save the path in localStorage, so that copying from 2 different From 42a03c64c7f7bba70eb5c25af8e9d851073b461a Mon Sep 17 00:00:00 2001 From: Amin Yahyaabadi Date: Thu, 8 Apr 2021 21:12:15 -0500 Subject: [PATCH 12/14] test: add onWillDeleteEntry, onEntryDeleted, finishRemoval emitter calls --- spec/tree-view-package-spec.coffee | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/spec/tree-view-package-spec.coffee b/spec/tree-view-package-spec.coffee index f6249d70..e19a0971 100644 --- a/spec/tree-view-package-spec.coffee +++ b/spec/tree-view-package-spec.coffee @@ -3159,8 +3159,15 @@ describe "TreeView", -> expect(args.buttons).toEqual ['Permanently Delete ⚠️', 'Cancel'] - it "calls removeSelectedEntries and removeSelectedPathsPermanently", -> + it "calls removeSelectedPathsPermanently, onWillDeleteEntry, onEntryDeleted, finishRemoval", -> spyOn(atom, 'confirm').andCallFake (options, callback) -> callback(0) + onEntryDeletedSpy = jasmine.createSpy('onEntryDeleted') + treeView.onEntryDeleted(onEntryDeletedSpy) + + onWillDeleteEntrySpy = jasmine.createSpy('onWillDeleteEntry') + treeView.onWillDeleteEntry(onWillDeleteEntrySpy) + + finishRemovalSpy = spyOn(treeView, 'finishRemoval').andCallThrough() removeSelectedPathsPermanentlySpy = spyOnAsyncAndCallThrough(treeView, 'removeSelectedPathsPermanently') removeSelectedEntriesSpy = spyOn(treeView, 'removeSelectedEntries').andCallThrough() @@ -3182,6 +3189,19 @@ describe "TreeView", -> removeSelectedEntriesSpy.mostRecentCall.args[0] is true and removeSelectedPathsPermanentlySpy.calledWith[0] is [filePath] + # The internal functionality of the followings are already tested in treeview:remove + waitsFor 'it calls onWillDeleteEntry', -> + onWillDeleteEntrySpy.callCount is 1 and + onWillDeleteEntrySpy.mostRecentCall.args[0] is {pathToDelete: filePath} + + waitsFor 'it calls onEntryDeleted', -> + onEntryDeletedSpy.callCount is 1 and + onEntryDeletedSpy.mostRecentCall.args[0] is {pathToDelete: filePath} + + waitsFor 'it calls finishRemoval', -> + finishRemovalSpy.callCount is 1 and + finishRemovalSpy.mostRecentCall.args[0] is removeSelectedPathsPermanentlySpy.calledWith[1][0] + describe "file system events", -> temporaryFilePath = null From 52a572b3ca336556f694fcad2d382a3fcada4023 Mon Sep 17 00:00:00 2001 From: Amin Yahyaabadi Date: Fri, 9 Apr 2021 00:18:49 -0500 Subject: [PATCH 13/14] test: fix the failing tests --- spec/async-helper.js | 32 ------------------------------ spec/tree-view-package-spec.coffee | 14 +++++-------- 2 files changed, 5 insertions(+), 41 deletions(-) delete mode 100644 spec/async-helper.js diff --git a/spec/async-helper.js b/spec/async-helper.js deleted file mode 100644 index 7d0b23a9..00000000 --- a/spec/async-helper.js +++ /dev/null @@ -1,32 +0,0 @@ -/** - * Spy on an async method and call through - * @param obj {Record} - * @param method {string} - * @returns {Jasmine.Spy & {resolvedWith: any, calledWith: Array}} - */ -function spyOnAsyncAndCallThrough (obj, method) { - const originalMethod = obj[method] - if (typeof originalMethod !== 'function') { - throw new Error(`${method} is not a method of ${obj}`) - } - let resolvedWith - let calledWith - let asyncSpy = spyOn(obj, method) - .andCallFake((...args) => { - calledWith = args - originalMethod.call(obj, ...args) - .then((returnValue) => { - resolvedWith = returnValue - // update spy call information - asyncSpy.resolvedWith = resolvedWith - asyncSpy.calledWith = calledWith - }).catch((err) => { - throw err - }) - }) - // initial undefined values - asyncSpy.resolvedWith = resolvedWith - asyncSpy.calledWith = calledWith - return asyncSpy -} -exports.spyOnAsyncAndCallThrough = spyOnAsyncAndCallThrough diff --git a/spec/tree-view-package-spec.coffee b/spec/tree-view-package-spec.coffee index e19a0971..3d996799 100644 --- a/spec/tree-view-package-spec.coffee +++ b/spec/tree-view-package-spec.coffee @@ -6,7 +6,6 @@ os = require 'os' {remote, shell} = require 'electron' Directory = require '../lib/directory' eventHelpers = require "./event-helpers" -spyOnAsyncAndCallThrough = require('./async-helper').spyOnAsyncAndCallThrough isCaseSensitive = null isFilesystemCaseSensitive = -> @@ -3169,7 +3168,7 @@ describe "TreeView", -> finishRemovalSpy = spyOn(treeView, 'finishRemoval').andCallThrough() - removeSelectedPathsPermanentlySpy = spyOnAsyncAndCallThrough(treeView, 'removeSelectedPathsPermanently') + removeSelectedPathsPermanentlySpy = spyOn(treeView, 'removeSelectedPathsPermanently').andCallThrough() removeSelectedEntriesSpy = spyOn(treeView, 'removeSelectedEntries').andCallThrough() filePath = path.join(os.tmpdir(), 'non-project-file.txt') @@ -3181,26 +3180,23 @@ describe "TreeView", -> waitsForPromise -> atom.commands.dispatch(treeView.element, 'tree-view:remove-permanently') - waitsFor -> - removeSelectedPathsPermanentlySpy.calledWith isnt undefined - waitsFor 'removeSelectedEntries amd removeSelectedPathsPermanently to be called', -> removeSelectedEntriesSpy.callCount is 1 and removeSelectedEntriesSpy.mostRecentCall.args[0] is true and - removeSelectedPathsPermanentlySpy.calledWith[0] is [filePath] + removeSelectedPathsPermanentlySpy.mostRecentCall.args[0][0] is filePath # The internal functionality of the followings are already tested in treeview:remove waitsFor 'it calls onWillDeleteEntry', -> onWillDeleteEntrySpy.callCount is 1 and - onWillDeleteEntrySpy.mostRecentCall.args[0] is {pathToDelete: filePath} + onWillDeleteEntrySpy.mostRecentCall.args[0].pathToDelete is filePath waitsFor 'it calls onEntryDeleted', -> onEntryDeletedSpy.callCount is 1 and - onEntryDeletedSpy.mostRecentCall.args[0] is {pathToDelete: filePath} + onEntryDeletedSpy.mostRecentCall.args[0].pathToDelete is filePath waitsFor 'it calls finishRemoval', -> finishRemovalSpy.callCount is 1 and - finishRemovalSpy.mostRecentCall.args[0] is removeSelectedPathsPermanentlySpy.calledWith[1][0] + finishRemovalSpy.mostRecentCall.args[0] is removeSelectedPathsPermanentlySpy.mostRecentCall.args[1][0] describe "file system events", -> temporaryFilePath = null From cc20188cb07672cc448c87aff916abc6eeb9be34 Mon Sep 17 00:00:00 2001 From: Amin Yahyaabadi Date: Fri, 9 Apr 2021 00:30:09 -0500 Subject: [PATCH 14/14] test: convert waitsFor to runs --- spec/tree-view-package-spec.coffee | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/spec/tree-view-package-spec.coffee b/spec/tree-view-package-spec.coffee index 3d996799..14805447 100644 --- a/spec/tree-view-package-spec.coffee +++ b/spec/tree-view-package-spec.coffee @@ -3185,18 +3185,24 @@ describe "TreeView", -> removeSelectedEntriesSpy.mostRecentCall.args[0] is true and removeSelectedPathsPermanentlySpy.mostRecentCall.args[0][0] is filePath - # The internal functionality of the followings are already tested in treeview:remove - waitsFor 'it calls onWillDeleteEntry', -> - onWillDeleteEntrySpy.callCount is 1 and - onWillDeleteEntrySpy.mostRecentCall.args[0].pathToDelete is filePath - - waitsFor 'it calls onEntryDeleted', -> - onEntryDeletedSpy.callCount is 1 and - onEntryDeletedSpy.mostRecentCall.args[0].pathToDelete is filePath - - waitsFor 'it calls finishRemoval', -> - finishRemovalSpy.callCount is 1 and - finishRemovalSpy.mostRecentCall.args[0] is removeSelectedPathsPermanentlySpy.mostRecentCall.args[1][0] + runs: -> + # The internal functionality of the followings are already tested in treeview:remove + expect( + onWillDeleteEntrySpy.callCount is 1 and + onWillDeleteEntrySpy.mostRecentCall.args[0].pathToDelete is filePath + ).toBe(true, 'it calls onWillDeleteEntry') + + expect( + onEntryDeletedSpy.callCount is 1 and + onEntryDeletedSpy.mostRecentCall.args[0].pathToDelete is filePath + ).toBe(true, 'it calls onEntryDeleted') + + expect( + finishRemovalSpy.callCount is 1 and + finishRemovalSpy.mostRecentCall.args[0] is removeSelectedPathsPermanentlySpy.mostRecentCall.args[1][0] + ).toBe(true, 'it calls finishRemoval') + + expect(fs.existsSync(filePath)).toBe(false, 'it deletes the file') describe "file system events", -> temporaryFilePath = null