diff --git a/packages/zowe-explorer/CHANGELOG.md b/packages/zowe-explorer/CHANGELOG.md index 9820263bc..5e56b58a8 100644 --- a/packages/zowe-explorer/CHANGELOG.md +++ b/packages/zowe-explorer/CHANGELOG.md @@ -10,6 +10,9 @@ All notable changes to the "vscode-extension-for-zowe" extension will be documen - Fix issue where the Zowe Explorer VS Code command `Refresh Zowe Explorer` failed catastrophically. [#3100](https://github.com/zowe/zowe-explorer-vscode/issues/3100) - Fixed an issue where the `ProfilesUtils.getProfileInfo` function returned a new `ProfileInfo` instance that did not respect the `ZOWE_CLI_HOME` environment variable and workspace paths. [#3168](https://github.com/zowe/zowe-explorer-vscode/issues/3168) +- Fixed an issue where the location prompt for the `Create Directory` and `Create File` USS features would appear even when a path is already set for the profile or parent folder. [#3183](https://github.com/zowe/zowe-explorer-vscode/pull/3183) +- Fixed an issue where the `Create Directory` and `Create File` features would continue processing when the first prompt was dismissed, causing an incorrect path to be generated. [#3183](https://github.com/zowe/zowe-explorer-vscode/pull/3183) +- Fixed an issue where the `Create Directory` and `Create File` features would incorrectly handle user-specified locations with trailing slashes. [#3183](https://github.com/zowe/zowe-explorer-vscode/pull/3183) ## `2.18.0` diff --git a/packages/zowe-explorer/__tests__/__unit__/uss/actions.unit.test.ts b/packages/zowe-explorer/__tests__/__unit__/uss/actions.unit.test.ts index 3fff27529..4501c269f 100644 --- a/packages/zowe-explorer/__tests__/__unit__/uss/actions.unit.test.ts +++ b/packages/zowe-explorer/__tests__/__unit__/uss/actions.unit.test.ts @@ -197,14 +197,64 @@ describe("USS Action Unit Tests - Function createUSSNode", () => { globalMocks.mockShowInputBox.mockReturnValue("USSFolder"); jest.spyOn(blockMocks.ussNode, "getChildren").mockResolvedValueOnce([]); - const isTopLevel = false; jest.spyOn(refreshActions, "refreshAll"); + mocked(refreshActions.refreshAll).mockClear(); - await ussNodeActions.createUSSNode(blockMocks.ussNode, blockMocks.testUSSTree, "folder", isTopLevel); + await ussNodeActions.createUSSNode(blockMocks.ussNode, blockMocks.testUSSTree, "folder"); expect(blockMocks.testUSSTree.refreshElement).toHaveBeenCalled(); expect(refreshActions.refreshAll).not.toHaveBeenCalled(); }); + it("should prompt the user for a location if one is not set on the node", async () => { + const globalMocks = createGlobalMocks(); + const blockMocks = await createBlockMocks(globalMocks); + + globalMocks.mockShowInputBox.mockReturnValue("USSFolder"); + jest.spyOn(blockMocks.ussNode, "getChildren").mockResolvedValueOnce([]); + globalMocks.mockShowInputBox.mockResolvedValueOnce("/u/myuser/"); + globalMocks.mockShowInputBox.mockResolvedValueOnce("folderName"); + const createApiMock = jest.spyOn(blockMocks.ussApi, "create").mockImplementation(); + const refreshAllMock = jest.spyOn(refreshActions, "refreshAll").mockImplementation(); + blockMocks.ussNode.getParent().fullPath = ""; + + await ussNodeActions.createUSSNode(blockMocks.ussNode.getParent(), blockMocks.testUSSTree, "folder"); + expect(globalMocks.mockShowInputBox).toHaveBeenCalledTimes(2); + expect(refreshAllMock).toHaveBeenCalled(); + createApiMock.mockRestore(); + refreshAllMock.mockRestore(); + }); + + it("returns early if a location was never provided", async () => { + const globalMocks = createGlobalMocks(); + const blockMocks = await createBlockMocks(globalMocks); + + globalMocks.mockShowInputBox.mockResolvedValueOnce(undefined); + const createApiMock = jest.spyOn(blockMocks.ussApi, "create").mockImplementation(); + createApiMock.mockClear(); + blockMocks.ussNode.getParent().fullPath = ""; + + await ussNodeActions.createUSSNode(blockMocks.ussNode.getParent(), blockMocks.testUSSTree, "directory"); + expect(createApiMock).not.toHaveBeenCalled(); + createApiMock.mockRestore(); + }); + + it("handles trailing slashes in the location", async () => { + const globalMocks = createGlobalMocks(); + const blockMocks = await createBlockMocks(globalMocks); + + globalMocks.mockShowInputBox.mockResolvedValueOnce("/u/myuser/aDir/"); + globalMocks.mockShowInputBox.mockResolvedValueOnce("testFile.txt"); + const createApiMock = jest.spyOn(blockMocks.ussApi, "create").mockImplementation(); + const refreshAllMock = jest.spyOn(refreshActions, "refreshAll").mockImplementation(); + blockMocks.ussNode.getParent().fullPath = ""; + + await ussNodeActions.createUSSNode(blockMocks.ussNode.getParent(), blockMocks.testUSSTree, "file"); + expect(createApiMock).toHaveBeenCalledWith("/u/myuser/aDir/testFile.txt", "file"); + expect(refreshAllMock).toHaveBeenCalled(); + createApiMock.mockRestore(); + refreshAllMock.mockRestore(); + }); + it("Tests that createUSSNode does not execute if node name was not entered", async () => { const globalMocks = createGlobalMocks(); const blockMocks = await createBlockMocks(globalMocks); @@ -262,11 +312,10 @@ describe("USS Action Unit Tests - Function createUSSNode", () => { const blockMocks = await createBlockMocks(globalMocks); globalMocks.mockShowInputBox.mockReturnValueOnce("USSFolder"); - jest.spyOn(blockMocks.ussNode, "getChildren").mockResolvedValueOnce([]); - const isTopLevel = false; jest.spyOn(refreshActions, "refreshAll"); + mocked(refreshActions.refreshAll).mockClear(); - await ussNodeActions.createUSSNode(blockMocks.ussNode, blockMocks.testUSSTree, "folder", isTopLevel); + await ussNodeActions.createUSSNode(blockMocks.ussNode, blockMocks.testUSSTree, "folder"); expect(blockMocks.testUSSTree.refreshElement).toHaveBeenCalled(); expect(refreshActions.refreshAll).not.toHaveBeenCalled(); }); @@ -275,7 +324,6 @@ describe("USS Action Unit Tests - Function createUSSNode", () => { const globalMocks = createGlobalMocks(); const blockMocks = await createBlockMocks(globalMocks); globalMocks.mockShowInputBox.mockReturnValueOnce("USSFolder"); - const isTopLevel = false; const errorHandlingSpy = jest.spyOn(utils, "errorHandling"); // Simulate unsuccessful api call @@ -285,7 +333,7 @@ describe("USS Action Unit Tests - Function createUSSNode", () => { }), }); - await expect(ussNodeActions.createUSSNode(blockMocks.ussNode, blockMocks.testUSSTree, "folder", isTopLevel)).rejects.toThrow(); + await expect(ussNodeActions.createUSSNode(blockMocks.ussNode, blockMocks.testUSSTree, "folder")).rejects.toThrow(); expect(errorHandlingSpy).toHaveBeenCalledTimes(1); }); }); diff --git a/packages/zowe-explorer/src/uss/actions.ts b/packages/zowe-explorer/src/uss/actions.ts index dd8742169..09f1281b0 100644 --- a/packages/zowe-explorer/src/uss/actions.ts +++ b/packages/zowe-explorer/src/uss/actions.ts @@ -45,16 +45,12 @@ const localize: nls.LocalizeFunc = nls.loadMessageBundle(); * @param {ussTree} ussFileProvider - Current ussTree used to populate the TreeView * @returns {Promise} */ -export async function createUSSNode( - node: IZoweUSSTreeNode, - ussFileProvider: IZoweTree, - nodeType: string, - isTopLevel?: boolean -): Promise { +export async function createUSSNode(node: IZoweUSSTreeNode, ussFileProvider: IZoweTree, nodeType: string): Promise { ZoweLogger.trace("uss.actions.createUSSNode called."); await ussFileProvider.checkCurrentProfile(node); let filePath = ""; - if (contextually.isSession(node)) { + const isTopLevel = contextually.isSession(node); + if (isTopLevel && node.fullPath?.length === 0) { const filePathOptions: vscode.InputBoxOptions = { placeHolder: localize("createUSSNode.inputBox.placeholder", "{0} location", nodeType), prompt: localize("createUSSNode.inputBox.prompt", "Choose a location to create the {0}", nodeType), @@ -64,13 +60,18 @@ export async function createUSSNode( } else { filePath = node.fullPath; } + + if (filePath == null || filePath.length === 0) { + return; + } + const nameOptions: vscode.InputBoxOptions = { placeHolder: localize("createUSSNode.name", "Name of file or directory"), }; const name = await Gui.showInputBox(nameOptions); if (name && filePath) { try { - filePath = `${filePath}/${name}`; + filePath = path.posix.join(filePath, name); await ZoweExplorerApiRegister.getUssApi(node.getProfile()).create(filePath, nodeType); if (isTopLevel) { await refreshAll(ussFileProvider);