diff --git a/packages/zowe-explorer-api/__tests__/__unit__/globals/Gui.unit.test.ts b/packages/zowe-explorer-api/__tests__/__unit__/globals/Gui.unit.test.ts index 28a11d19cc..a8382a571a 100644 --- a/packages/zowe-explorer-api/__tests__/__unit__/globals/Gui.unit.test.ts +++ b/packages/zowe-explorer-api/__tests__/__unit__/globals/Gui.unit.test.ts @@ -22,7 +22,7 @@ function createGlobalMocks() { showWarningMessage: jest.fn(), createOutputChannel: jest.fn(), createQuickPick: jest.fn(), - createTreeView: jest.fn(), + createTreeView: jest.fn().mockReturnValue({ onDidCollapseElement: jest.fn() }), createWebviewPanel: jest.fn(), withProgress: jest.fn(), showTextDocument: jest.fn(), diff --git a/packages/zowe-explorer/CHANGELOG.md b/packages/zowe-explorer/CHANGELOG.md index b0125ababf..6f5185e52d 100644 --- a/packages/zowe-explorer/CHANGELOG.md +++ b/packages/zowe-explorer/CHANGELOG.md @@ -9,6 +9,10 @@ All notable changes to the "vscode-extension-for-zowe" extension will be documen ### Bug fixes - Fixed submitting local JCL using command pallet option `Zowe Explorer: Submit JCL` by adding a check for chosen profile returned to continue the action. [#1625](https://github.com/zowe/vscode-extension-for-zowe/issues/1625) +- Fixed issue where USS nodes were not removed from tree during deletion. [#2479](https://github.com/zowe/vscode-extension-for-zowe/issues/2479) +- Fixed issue where new USS nodes from a paste operation were not shown in tree until refreshed. [#2479](https://github.com/zowe/vscode-extension-for-zowe/issues/2479) +- Fixed issue where the "Delete Job" action showed a successful deletion message, even if the API returned an error. +- USS directories, PDS nodes, job nodes and session nodes now update with their respective "collapsed icon" when collapsed. - Fixed bug where the list of datasets from a filter search was not re-sorted after a new data set was created in Zowe Explorer. [#2473](https://github.com/zowe/vscode-extension-for-zowe/issues/2473) ## `2.11.0` diff --git a/packages/zowe-explorer/__mocks__/vscode.ts b/packages/zowe-explorer/__mocks__/vscode.ts index c479dbf6c4..5cc8a04263 100644 --- a/packages/zowe-explorer/__mocks__/vscode.ts +++ b/packages/zowe-explorer/__mocks__/vscode.ts @@ -144,7 +144,12 @@ export namespace extensions { }; } } - +export interface TreeViewExpansionEvent { + /** + * Element that is expanded or collapsed. + */ + readonly element: T; +} export interface TreeView { /** * An optional human-readable message that will be rendered in the view. @@ -177,6 +182,8 @@ export interface TreeView { * **NOTE:** The {@link TreeDataProvider} that the `TreeView` {@link window.createTreeView is registered with} with must implement {@link TreeDataProvider.getParent getParent} method to access this API. */ reveal(element: T, options?: { select?: boolean; focus?: boolean; expand?: boolean | number }): Thenable; + + onDidCollapseElement: Event>; } export class FileDecoration { diff --git a/packages/zowe-explorer/__tests__/__unit__/Profiles.extended.unit.test.ts b/packages/zowe-explorer/__tests__/__unit__/Profiles.extended.unit.test.ts index 2876032d7b..5309decf5a 100644 --- a/packages/zowe-explorer/__tests__/__unit__/Profiles.extended.unit.test.ts +++ b/packages/zowe-explorer/__tests__/__unit__/Profiles.extended.unit.test.ts @@ -112,7 +112,10 @@ async function createGlobalMocks() { configurable: true, }); Object.defineProperty(globals, "ISTHEIA", { get: () => false, configurable: true }); - Object.defineProperty(vscode.window, "createTreeView", { value: jest.fn(), configurable: true }); + Object.defineProperty(vscode.window, "createTreeView", { + value: jest.fn().mockReturnValue({ onDidCollapseElement: jest.fn() }), + configurable: true, + }); Object.defineProperty(vscode.workspace, "getConfiguration", { value: newMocks.mockGetConfiguration, configurable: true, diff --git a/packages/zowe-explorer/__tests__/__unit__/ZoweExplorerExtender.unit.test.ts b/packages/zowe-explorer/__tests__/__unit__/ZoweExplorerExtender.unit.test.ts index 6ae337f750..34cbb87bb6 100644 --- a/packages/zowe-explorer/__tests__/__unit__/ZoweExplorerExtender.unit.test.ts +++ b/packages/zowe-explorer/__tests__/__unit__/ZoweExplorerExtender.unit.test.ts @@ -53,7 +53,10 @@ describe("ZoweExplorerExtender unit tests", () => { }) .mockReturnValue(newMocks.profiles), }); - Object.defineProperty(vscode.window, "createTreeView", { value: jest.fn(), configurable: true }); + Object.defineProperty(vscode.window, "createTreeView", { + value: jest.fn().mockReturnValue({ onDidCollapseElement: jest.fn() }), + configurable: true, + }); Object.defineProperty(vscode.window, "showErrorMessage", { value: newMocks.mockErrorMessage, configurable: true, diff --git a/packages/zowe-explorer/__tests__/__unit__/ZoweNode.unit.test.ts b/packages/zowe-explorer/__tests__/__unit__/ZoweNode.unit.test.ts index 3bc557fb9f..72da57e2c7 100644 --- a/packages/zowe-explorer/__tests__/__unit__/ZoweNode.unit.test.ts +++ b/packages/zowe-explorer/__tests__/__unit__/ZoweNode.unit.test.ts @@ -238,7 +238,6 @@ describe("Unit Tests (Jest)", () => { undefined, profileOne ); - infoChild.id = "root.Use the search button to display data sets"; rootNode.contextValue = globals.DS_SESSION_CONTEXT; rootNode.dirty = false; await expect(await rootNode.getChildren()).toEqual([infoChild]); @@ -259,7 +258,6 @@ describe("Unit Tests (Jest)", () => { undefined, profileOne ); - infoChild.id = "root.Use the search button to display data sets"; rootNode.contextValue = globals.DS_SESSION_CONTEXT; await expect(await rootNode.getChildren()).toEqual([infoChild]); }); diff --git a/packages/zowe-explorer/__tests__/__unit__/abstract/TreeProvider.unit.test.ts b/packages/zowe-explorer/__tests__/__unit__/abstract/TreeProvider.unit.test.ts index 096d54cf5b..0771a57f74 100644 --- a/packages/zowe-explorer/__tests__/__unit__/abstract/TreeProvider.unit.test.ts +++ b/packages/zowe-explorer/__tests__/__unit__/abstract/TreeProvider.unit.test.ts @@ -37,7 +37,7 @@ async function createGlobalMocks() { mockLoadNamedProfile: jest.fn(), mockDefaultProfile: jest.fn(), withProgress: jest.fn(), - createTreeView: jest.fn(), + createTreeView: jest.fn().mockReturnValue({ onDidCollapseElement: jest.fn() }), mockAffects: jest.fn(), mockEditSession: jest.fn(), mockCheckCurrentProfile: jest.fn(), diff --git a/packages/zowe-explorer/__tests__/__unit__/abstract/ZoweSaveQueue.unit.test.ts b/packages/zowe-explorer/__tests__/__unit__/abstract/ZoweSaveQueue.unit.test.ts index 048fb60b96..d50db952a2 100644 --- a/packages/zowe-explorer/__tests__/__unit__/abstract/ZoweSaveQueue.unit.test.ts +++ b/packages/zowe-explorer/__tests__/__unit__/abstract/ZoweSaveQueue.unit.test.ts @@ -20,6 +20,7 @@ import { ZoweLogger } from "../../../src/utils/LoggerUtils"; describe("ZoweSaveQueue - unit tests", () => { const createGlobalMocks = () => { + jest.spyOn(Gui, "createTreeView").mockReturnValue({ onDidCollapseElement: jest.fn() } as any); const globalMocks = { errorMessageSpy: jest.spyOn(Gui, "errorMessage"), markDocumentUnsavedSpy: jest.spyOn(workspaceUtils, "markDocumentUnsaved"), diff --git a/packages/zowe-explorer/__tests__/__unit__/dataset/DatasetTree.unit.test.ts b/packages/zowe-explorer/__tests__/__unit__/dataset/DatasetTree.unit.test.ts index 9cb5357619..42d3a43fc0 100644 --- a/packages/zowe-explorer/__tests__/__unit__/dataset/DatasetTree.unit.test.ts +++ b/packages/zowe-explorer/__tests__/__unit__/dataset/DatasetTree.unit.test.ts @@ -59,7 +59,10 @@ function createGlobalMocks() { globalMocks.mockProfileInstance = createInstanceOfProfile(globalMocks.testProfileLoaded); - Object.defineProperty(vscode.window, "createTreeView", { value: jest.fn(), configurable: true }); + Object.defineProperty(vscode.window, "createTreeView", { + value: jest.fn().mockReturnValue({ onDidCollapseElement: jest.fn() }), + configurable: true, + }); Object.defineProperty(Gui, "showMessage", { value: jest.fn(), configurable: true }); Object.defineProperty(Gui, "setStatusBarMessage", { value: jest.fn().mockReturnValue({ dispose: jest.fn() }), configurable: true }); Object.defineProperty(vscode.window, "showTextDocument", { value: jest.fn(), configurable: true }); diff --git a/packages/zowe-explorer/__tests__/__unit__/extension.unit.test.ts b/packages/zowe-explorer/__tests__/__unit__/extension.unit.test.ts index 1b8e377e58..f038b1e853 100644 --- a/packages/zowe-explorer/__tests__/__unit__/extension.unit.test.ts +++ b/packages/zowe-explorer/__tests__/__unit__/extension.unit.test.ts @@ -45,7 +45,7 @@ async function createGlobalMocks() { mockMoveSync: jest.fn(), mockGetAllProfileNames: jest.fn(), mockReveal: jest.fn(), - mockCreateTreeView: jest.fn(), + mockCreateTreeView: jest.fn().mockReturnValue({ onDidCollapseElement: jest.fn() }), mockExecuteCommand: jest.fn(), mockRegisterCommand: jest.fn(), mockOnDidSaveTextDocument: jest.fn(), diff --git a/packages/zowe-explorer/__tests__/__unit__/generators/icons.unit.test.ts b/packages/zowe-explorer/__tests__/__unit__/generators/icons.unit.test.ts index 957c70970c..c9ec8046f2 100644 --- a/packages/zowe-explorer/__tests__/__unit__/generators/icons.unit.test.ts +++ b/packages/zowe-explorer/__tests__/__unit__/generators/icons.unit.test.ts @@ -19,7 +19,7 @@ import * as vscode from "vscode"; describe("Checking icon generator's basics", () => { const setGlobalMocks = () => { - const createTreeView = jest.fn(); + const createTreeView = jest.fn().mockReturnValue({ onDidCollapseElement: jest.fn() }); const getConfiguration = jest.fn(); Object.defineProperty(vscode.window, "createTreeView", { value: createTreeView }); diff --git a/packages/zowe-explorer/__tests__/__unit__/generators/messages.unit.test.ts b/packages/zowe-explorer/__tests__/__unit__/generators/messages.unit.test.ts index 2b46ac7d6a..a4fd28edc8 100644 --- a/packages/zowe-explorer/__tests__/__unit__/generators/messages.unit.test.ts +++ b/packages/zowe-explorer/__tests__/__unit__/generators/messages.unit.test.ts @@ -19,7 +19,7 @@ jest.mock("vscode"); describe("Checking message generator's basics", () => { const setGlobalMocks = () => { - const createTreeView = jest.fn(); + const createTreeView = jest.fn().mockReturnValue({ onDidCollapseElement: jest.fn() }); const getConfiguration = jest.fn(); Object.defineProperty(vscode.window, "createTreeView", { value: createTreeView }); diff --git a/packages/zowe-explorer/__tests__/__unit__/job/ZosJobsProvider.unit.test.ts b/packages/zowe-explorer/__tests__/__unit__/job/ZosJobsProvider.unit.test.ts index da5b74ffa7..7965f68cef 100644 --- a/packages/zowe-explorer/__tests__/__unit__/job/ZosJobsProvider.unit.test.ts +++ b/packages/zowe-explorer/__tests__/__unit__/job/ZosJobsProvider.unit.test.ts @@ -45,7 +45,7 @@ async function createGlobalMocks() { mockGetJob: jest.fn(), mockRefresh: jest.fn(), mockAffectsConfig: jest.fn(), - createTreeView: jest.fn(), + createTreeView: jest.fn().mockReturnValue({ onDidCollapseElement: jest.fn() }), mockGetSpoolFiles: jest.fn(), mockDeleteJobs: jest.fn(), mockShowInputBox: jest.fn(), @@ -85,7 +85,7 @@ async function createGlobalMocks() { }; }), }; - + jest.spyOn(Gui, "createTreeView").mockImplementation(globalMocks.createTreeView); Object.defineProperty(ProfilesCache, "getConfigInstance", { value: jest.fn(() => { return { @@ -173,7 +173,6 @@ async function createGlobalMocks() { Object.defineProperty(ZoweLogger, "warn", { value: jest.fn(), configurable: true }); Object.defineProperty(ZoweLogger, "info", { value: jest.fn(), configurable: true }); Object.defineProperty(ZoweLogger, "trace", { value: jest.fn(), configurable: true }); - globalMocks.createTreeView.mockReturnValue("testTreeView"); globalMocks.testSessionNode = createJobSessionNode(globalMocks.testSession, globalMocks.testProfile); globalMocks.mockGetJob.mockReturnValue(globalMocks.testIJob); globalMocks.mockGetJobsByOwnerAndPrefix.mockReturnValue([globalMocks.testIJob, globalMocks.testIJobComplete]); diff --git a/packages/zowe-explorer/__tests__/__unit__/job/ZoweJobNode.unit.test.ts b/packages/zowe-explorer/__tests__/__unit__/job/ZoweJobNode.unit.test.ts index 2ab7070292..554aaf2670 100644 --- a/packages/zowe-explorer/__tests__/__unit__/job/ZoweJobNode.unit.test.ts +++ b/packages/zowe-explorer/__tests__/__unit__/job/ZoweJobNode.unit.test.ts @@ -40,6 +40,7 @@ async function createGlobalMocks() { mockAffectsConfig: jest.fn(), createTreeView: jest.fn(() => ({ reveal: jest.fn(), + onDidCollapseElement: jest.fn(), })), mockCreateSessCfgFromArgs: jest.fn(), mockGetSpoolFiles: jest.fn(), diff --git a/packages/zowe-explorer/__tests__/__unit__/job/actions.unit.test.ts b/packages/zowe-explorer/__tests__/__unit__/job/actions.unit.test.ts index 0c6847479e..2df3701b7d 100644 --- a/packages/zowe-explorer/__tests__/__unit__/job/actions.unit.test.ts +++ b/packages/zowe-explorer/__tests__/__unit__/job/actions.unit.test.ts @@ -45,6 +45,7 @@ import { SpoolFile } from "../../../src/SpoolProvider"; const activeTextEditorDocument = jest.fn(); function createGlobalMocks() { + jest.spyOn(Gui, "createTreeView").mockReturnValue({ onDidCollapseElement: jest.fn() } as any); Object.defineProperty(vscode.workspace, "getConfiguration", { value: jest.fn().mockImplementation(() => new Map([["zowe.jobs.confirmSubmission", false]])), configurable: true, diff --git a/packages/zowe-explorer/__tests__/__unit__/shared/actions.unit.test.ts b/packages/zowe-explorer/__tests__/__unit__/shared/actions.unit.test.ts index 51e6c042a8..aec9ead9cf 100644 --- a/packages/zowe-explorer/__tests__/__unit__/shared/actions.unit.test.ts +++ b/packages/zowe-explorer/__tests__/__unit__/shared/actions.unit.test.ts @@ -69,7 +69,10 @@ async function createGlobalMocks() { }), configurable: true, }); - Object.defineProperty(vscode.window, "createTreeView", { value: jest.fn(), configurable: true }); + Object.defineProperty(vscode.window, "createTreeView", { + value: jest.fn().mockReturnValue({ onDidCollapseElement: jest.fn() }), + configurable: true, + }); Object.defineProperty(vscode.workspace, "getConfiguration", { value: jest.fn(), configurable: true }); Object.defineProperty(vscode.window, "showInformationMessage", { value: jest.fn(), configurable: true }); Object.defineProperty(vscode.window, "showInputBox", { value: jest.fn(), configurable: true }); diff --git a/packages/zowe-explorer/__tests__/__unit__/shared/refresh.unit.test.ts b/packages/zowe-explorer/__tests__/__unit__/shared/refresh.unit.test.ts index f306801e88..7dc0661da9 100644 --- a/packages/zowe-explorer/__tests__/__unit__/shared/refresh.unit.test.ts +++ b/packages/zowe-explorer/__tests__/__unit__/shared/refresh.unit.test.ts @@ -31,7 +31,7 @@ import { ZoweLogger } from "../../../src/utils/LoggerUtils"; function createGlobalMocks() { const globalMocks = { session: createISessionWithoutCredentials(), - createTreeView: jest.fn(), + createTreeView: jest.fn().mockReturnValue({ onDidCollapseElement: jest.fn() }), mockLog: jest.fn(), mockDebug: jest.fn(), mockError: jest.fn(), diff --git a/packages/zowe-explorer/__tests__/__unit__/uss/USSTree.unit.test.ts b/packages/zowe-explorer/__tests__/__unit__/uss/USSTree.unit.test.ts index 56d20ad5b0..8de2e871ab 100644 --- a/packages/zowe-explorer/__tests__/__unit__/uss/USSTree.unit.test.ts +++ b/packages/zowe-explorer/__tests__/__unit__/uss/USSTree.unit.test.ts @@ -47,7 +47,7 @@ async function createGlobalMocks() { showInputBox: jest.fn(), filters: jest.fn(), getFilters: jest.fn(), - createTreeView: jest.fn(), + createTreeView: jest.fn().mockReturnValue({ onDidCollapseElement: jest.fn() }), createQuickPick: jest.fn(), getConfiguration: jest.fn(), ZosmfSession: jest.fn(), diff --git a/packages/zowe-explorer/__tests__/__unit__/uss/ZoweUSSNode.unit.test.ts b/packages/zowe-explorer/__tests__/__unit__/uss/ZoweUSSNode.unit.test.ts index b80a6805f3..1a02dc1ea2 100644 --- a/packages/zowe-explorer/__tests__/__unit__/uss/ZoweUSSNode.unit.test.ts +++ b/packages/zowe-explorer/__tests__/__unit__/uss/ZoweUSSNode.unit.test.ts @@ -137,7 +137,10 @@ async function createGlobalMocks() { configurable: true, }); Object.defineProperty(vscode.window, "showInputBox", { value: globalMocks.showInputBox, configurable: true }); - Object.defineProperty(vscode.window, "createTreeView", { value: jest.fn(), configurable: true }); + Object.defineProperty(vscode.window, "createTreeView", { + value: jest.fn().mockReturnValue({ onDidCollapseElement: jest.fn() }), + configurable: true, + }); Object.defineProperty(zowe, "ZosmfSession", { value: globalMocks.ZosmfSession, configurable: true }); Object.defineProperty(globalMocks.ZosmfSession, "createSessCfgFromArgs", { value: globalMocks.createSessCfgFromArgs, 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 4c84b3d6ec..4d054e113a 100644 --- a/packages/zowe-explorer/__tests__/__unit__/uss/actions.unit.test.ts +++ b/packages/zowe-explorer/__tests__/__unit__/uss/actions.unit.test.ts @@ -60,7 +60,7 @@ function createGlobalMocks() { setStatusBarMessage: jest.fn().mockReturnValue({ dispose: jest.fn() }), showWarningMessage: jest.fn(), showErrorMessage: jest.fn(), - createTreeView: jest.fn(), + createTreeView: jest.fn().mockReturnValue({ onDidCollapseElement: jest.fn() }), fileToUSSFile: jest.fn(), Upload: jest.fn(), isBinaryFileSync: jest.fn(), @@ -891,9 +891,7 @@ describe("USS Action Unit Tests - copy file / directory", () => { it("tests pasteUssFile executed successfully with selected nodes", async () => { const globalMocks = createGlobalMocks(); const blockMocks = await createBlockMocks(globalMocks); - const parent = blockMocks.treeNodes.testUSSTree.getTreeView(); - parent.selection = blockMocks.nodes[0]; - await ussNodeActions.pasteUssFile(blockMocks.treeNodes.testUSSTree, undefined); + await ussNodeActions.pasteUssFile(blockMocks.treeNodes.testUSSTree, blockMocks.nodes[0]); expect(sharedUtils.getSelectedNodeList(blockMocks.treeNodes.ussNode, blockMocks.treeNodes.ussNodes)).toEqual([blockMocks.treeNodes.ussNode]); }); it("tests pasteUssFile executed successfully with one node", async () => { @@ -905,6 +903,16 @@ describe("USS Action Unit Tests - copy file / directory", () => { await ussNodeActions.pasteUssFile(blockMocks.treeNodes.testUSSTree, blockMocks.nodes[0]); expect(sharedUtils.getSelectedNodeList(blockMocks.treeNodes.ussNode, blockMocks.treeNodes.ussNodes)).toEqual([blockMocks.treeNodes.ussNode]); }); + it("tests pasteUss returns early if APIs are not supported", async () => { + const globalMocks = createGlobalMocks(); + const blockMocks = await createBlockMocks(globalMocks); + const testNode = blockMocks.nodes[0]; + testNode.copyUssFile = testNode.pasteUssTree = null; + const infoMessageSpy = jest.spyOn(Gui, "infoMessage"); + await ussNodeActions.pasteUss(blockMocks.treeNodes.testUSSTree, testNode); + expect(infoMessageSpy).toHaveBeenCalledWith("The paste operation is not supported for this node."); + infoMessageSpy.mockRestore(); + }); }); describe("USS Action Unit Tests - function deleteUSSFilesPrompt", () => { diff --git a/packages/zowe-explorer/__tests__/__unit__/utils/SessionUtils.unit.test.ts b/packages/zowe-explorer/__tests__/__unit__/utils/SessionUtils.unit.test.ts index c97166e6de..4925d06b58 100644 --- a/packages/zowe-explorer/__tests__/__unit__/utils/SessionUtils.unit.test.ts +++ b/packages/zowe-explorer/__tests__/__unit__/utils/SessionUtils.unit.test.ts @@ -29,7 +29,10 @@ describe("SessionUtils removeSession Unit Tests", () => { newMocks.datasetSessionNode = createDatasetSessionNode(newMocks.session, newMocks.imperativeProfile); newMocks.testDatasetTree = createDatasetTree(newMocks.datasetSessionNode, newMocks.treeView); newMocks.testDatasetTree.addFileHistory("[profile1]: TEST.NODE"); - Object.defineProperty(vscode.window, "createTreeView", { value: jest.fn(), configurable: true }); + Object.defineProperty(vscode.window, "createTreeView", { + value: jest.fn().mockReturnValue({ onDidCollapseElement: jest.fn() }), + configurable: true, + }); Object.defineProperty(vscode, "ConfigurationTarget", { value: jest.fn(), configurable: true }); newMocks.mockGetConfiguration.mockReturnValue(createPersistentConfig()); Object.defineProperty(vscode.workspace, "getConfiguration", { diff --git a/packages/zowe-explorer/__tests__/__unit__/utils/TreeViewUtils.unit.test.ts b/packages/zowe-explorer/__tests__/__unit__/utils/TreeViewUtils.unit.test.ts new file mode 100644 index 0000000000..cbb3cf4e4b --- /dev/null +++ b/packages/zowe-explorer/__tests__/__unit__/utils/TreeViewUtils.unit.test.ts @@ -0,0 +1,26 @@ +/** + * This program and the accompanying materials are made available under the terms of the + * Eclipse Public License v2.0 which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-v20.html + * + * SPDX-License-Identifier: EPL-2.0 + * + * Copyright Contributors to the Zowe Project. + * + */ + +import { TreeViewUtils } from "../../../src/utils/TreeViewUtils"; +import * as globals from "../../../src/globals"; + +describe("TreeViewUtils Unit Tests", () => { + it("refreshIconOnCollapse - generated listener function works as intended", () => { + const testTreeProvider = { mOnDidChangeTreeData: { fire: jest.fn() } } as any; + const listenerFn = TreeViewUtils.refreshIconOnCollapse( + [(node): boolean => (node.contextValue as any).includes(globals.DS_PDS_CONTEXT) as boolean], + testTreeProvider + ); + const element = { label: "somenode", contextValue: globals.DS_PDS_CONTEXT } as any; + listenerFn({ element }); + expect(testTreeProvider.mOnDidChangeTreeData.fire).toHaveBeenCalledWith(element); + }); +}); diff --git a/packages/zowe-explorer/i18n/sample/src/uss/actions.i18n.json b/packages/zowe-explorer/i18n/sample/src/uss/actions.i18n.json index a4af6ec483..638518fe52 100644 --- a/packages/zowe-explorer/i18n/sample/src/uss/actions.i18n.json +++ b/packages/zowe-explorer/i18n/sample/src/uss/actions.i18n.json @@ -12,5 +12,6 @@ "deleteUssPrompt.confirmation.message": "Are you sure you want to delete the following item?\nThis will permanently remove the following file or folder from your system.\n\n{0}", "deleteUssPrompt.confirmation.cancel.log.debug": "Delete action was canceled.", "ZoweUssNode.copyDownload.progress": "Copying file structure...", - "ZoweUssNode.copyUpload.progress": "Pasting files..." + "ZoweUssNode.copyUpload.progress": "Pasting files...", + "uss.paste.apiNotAvailable": "The paste operation is not supported for this node." } diff --git a/packages/zowe-explorer/src/dataset/DatasetTree.ts b/packages/zowe-explorer/src/dataset/DatasetTree.ts index 41e428768d..04e71c832b 100644 --- a/packages/zowe-explorer/src/dataset/DatasetTree.ts +++ b/packages/zowe-explorer/src/dataset/DatasetTree.ts @@ -96,6 +96,7 @@ export class DatasetTree extends ZoweTreeProvider implements IZoweTree"}.${this.label as string}`; } } @@ -256,10 +256,14 @@ export class ZoweDatasetNode extends ZoweTreeNode implements IZoweDatasetTreeNod .filter((label) => this.children.find((c) => (c.label as string) === label) == null) .map((label) => elementChildren[label]); - this.children = this.children - .concat(newChildren) - .filter((c) => (c.label as string) in elementChildren) - .sort((a, b) => ((a.label as string) < (b.label as string) ? -1 : 1)); + const removedChildren = this.children.filter((c) => !((c.label as string) in elementChildren)); + + if (newChildren.length > 0 || removedChildren.length > 0) { + this.children = this.children + .concat(newChildren) + .filter((c) => !removedChildren.includes(c)) + .sort((a, b) => ((a.label as string) < (b.label as string) ? -1 : 1)); + } } return this.children; diff --git a/packages/zowe-explorer/src/job/ZosJobsProvider.ts b/packages/zowe-explorer/src/job/ZosJobsProvider.ts index 3333724e6f..6ed92bd781 100644 --- a/packages/zowe-explorer/src/job/ZosJobsProvider.ts +++ b/packages/zowe-explorer/src/job/ZosJobsProvider.ts @@ -139,6 +139,7 @@ export class ZosJobsProvider extends ZoweTreeProvider implements IZoweTree"}.${this.label as string}`; } } diff --git a/packages/zowe-explorer/src/job/actions.ts b/packages/zowe-explorer/src/job/actions.ts index 06fecac4c5..c2da192d39 100644 --- a/packages/zowe-explorer/src/job/actions.ts +++ b/packages/zowe-explorer/src/job/actions.ts @@ -387,8 +387,6 @@ async function deleteSingleJob(job: IZoweJobTreeNode, jobsProvider: IZoweTree, jobsProvider: IZoweTree): Promise { diff --git a/packages/zowe-explorer/src/shared/utils.ts b/packages/zowe-explorer/src/shared/utils.ts index 98b6a293ad..cf10fac17b 100644 --- a/packages/zowe-explorer/src/shared/utils.ts +++ b/packages/zowe-explorer/src/shared/utils.ts @@ -138,7 +138,7 @@ function appendSuffix(label: string): string { const bracket = label.indexOf("("); const split = bracket > -1 ? label.substr(0, bracket).split(".", limit) : label.split(".", limit); for (let i = split.length - 1; i > 0; i--) { - if (["JCL", "CNTL"].includes(split[i])) { + if (["JCL", "JCLLIB", "CNTL"].includes(split[i])) { return label.concat(".jcl"); } if (["COBOL", "CBL", "COB", "SCBL"].includes(split[i])) { diff --git a/packages/zowe-explorer/src/uss/USSTree.ts b/packages/zowe-explorer/src/uss/USSTree.ts index c53a3b3fd3..d7bdc46ebe 100644 --- a/packages/zowe-explorer/src/uss/USSTree.ts +++ b/packages/zowe-explorer/src/uss/USSTree.ts @@ -80,6 +80,7 @@ export class USSTree extends ZoweTreeProvider implements IZoweTree 0 && this.prevPath !== this.fullPath) { + this.children = []; + } + // Build a list of nodes based on the API response const responseNodes: IZoweUSSTreeNode[] = []; - const newNodeCreated: boolean = response.apiResponse.items.reduce((lastResult: boolean, item) => { + for (const item of response.apiResponse.items) { if (item.name === "." || item.name === "..") { - return lastResult || false; + continue; } const existing = this.children.find( @@ -213,7 +218,7 @@ export class ZoweUSSNode extends ZoweTreeNode implements IZoweUSSTreeNode { }; responseNodes.push(existing); existing.onUpdateEmitter.fire(existing); - return lastResult || false; + continue; } if (item.mode.startsWith("d")) { @@ -253,20 +258,6 @@ export class ZoweUSSNode extends ZoweTreeNode implements IZoweUSSTreeNode { }; responseNodes.push(temp); } - - return lastResult || true; - }, false); - - this.dirty = false; - - // If no new nodes were created, return the cached list of children - if (!newNodeCreated) { - return this.children; - } - - // If search path has changed, invalidate all children - if (this.fullPath?.length > 0 && this.prevPath !== this.fullPath) { - this.children = []; } const nodesToAdd = responseNodes.filter((c) => !this.children.includes(c)); @@ -277,6 +268,7 @@ export class ZoweUSSNode extends ZoweTreeNode implements IZoweUSSTreeNode { .filter((c) => !nodesToRemove.includes(c)) .sort((a, b) => ((a.label as string) < (b.label as string) ? -1 : 1)); this.prevPath = this.fullPath; + this.dirty = false; return this.children; } @@ -749,10 +741,9 @@ export class ZoweUSSNode extends ZoweTreeNode implements IZoweUSSTreeNode { } const prof = this.getProfile(); - const remotePath = this.fullPath; try { + const fileTreeToPaste: UssFileTree = JSON.parse(clipboardContents); const api = ZoweExplorerApiRegister.getUssApi(this.profile); - const fileTreeToPaste: UssFileTree = JSON.parse(await vscode.env.clipboard.readText()); const sessionName = this.getSessionNode().getLabel() as string; const task: imperative.ITaskWithStatus = { @@ -767,7 +758,7 @@ export class ZoweUSSNode extends ZoweTreeNode implements IZoweUSSTreeNode { }; for (const subnode of fileTreeToPaste.children) { - await this.paste(sessionName, remotePath, { api, tree: subnode, options }); + await this.paste(sessionName, this.fullPath, { api, tree: subnode, options }); } } catch (error) { await errorHandling(error, this.label.toString(), localize("copyUssFile.error", "Error uploading files")); diff --git a/packages/zowe-explorer/src/uss/actions.ts b/packages/zowe-explorer/src/uss/actions.ts index c1ac4833f3..8ff901468d 100644 --- a/packages/zowe-explorer/src/uss/actions.ts +++ b/packages/zowe-explorer/src/uss/actions.ts @@ -487,21 +487,18 @@ export async function pasteUssFile(ussFileProvider: IZoweTree, */ export async function pasteUss(ussFileProvider: IZoweTree, node: IZoweUSSTreeNode): Promise { ZoweLogger.trace("uss.actions.pasteUss called."); - const a = ussFileProvider.getTreeView().selection as IZoweUSSTreeNode[]; - let selectedNode = node; - if (!selectedNode) { - selectedNode = a.length > 0 ? a[0] : (a as unknown as IZoweUSSTreeNode); + if (node.pasteUssTree == null && node.copyUssFile == null) { + await Gui.infoMessage(localize("uss.paste.apiNotAvailable", "The paste operation is not supported for this node.")); + return; } - await Gui.withProgress( { location: vscode.ProgressLocation.Window, title: localize("ZoweUssNode.copyUpload.progress", "Pasting files..."), }, async () => { - await (selectedNode.pasteUssTree ? selectedNode.pasteUssTree() : selectedNode.copyUssFile()); + await (node.pasteUssTree ? node.pasteUssTree() : node.copyUssFile()); } ); - const nodeToRefresh = node?.contextValue != null && contextually.isUssSession(node) ? selectedNode : selectedNode.getParent(); - ussFileProvider.refreshElement(nodeToRefresh); + ussFileProvider.refreshElement(node); } diff --git a/packages/zowe-explorer/src/utils/TreeViewUtils.ts b/packages/zowe-explorer/src/utils/TreeViewUtils.ts index a6d1f27df4..652e528fe5 100644 --- a/packages/zowe-explorer/src/utils/TreeViewUtils.ts +++ b/packages/zowe-explorer/src/utils/TreeViewUtils.ts @@ -11,6 +11,9 @@ import { IZoweNodeType, IZoweTree, IZoweTreeNode } from "@zowe/zowe-explorer-api"; import { ZoweLogger } from "./LoggerUtils"; +import { TreeViewExpansionEvent } from "vscode"; +import { getIconByNode } from "../generators/icons"; +import { ZoweTreeProvider } from "../abstract/ZoweTreeProvider"; export class TreeViewUtils { /** @@ -33,4 +36,21 @@ export class TreeViewUtils { ZoweLogger.trace("ZoweTreeProvider.expandNode called."); await provider.getTreeView().reveal(node, { expand: true }); } + + /** + * Builds an onDidCollapseElement event listener that will refresh node icons depending on the qualifiers given. + * If at least one node qualifier passes, it will refresh the icon for that node. + * @param qualifiers an array of boolean functions that take a tree node as a parameter + * @param treeProvider The tree provider that should update once the icons are changed + * @returns An event listener built to update the node icons based on the given qualifiers + */ + public static refreshIconOnCollapse(qualifiers: ((node: IZoweTreeNode) => boolean)[], treeProvider: ZoweTreeProvider) { + return (e: TreeViewExpansionEvent): any => { + const newIcon = getIconByNode(e.element); + if (qualifiers.some((q) => q(e.element)) && newIcon) { + e.element.iconPath = newIcon; + treeProvider.mOnDidChangeTreeData.fire(e.element); + } + }; + } }