From dda72a081d34591ab6d34e691f097b884ae91dc7 Mon Sep 17 00:00:00 2001 From: Trae Yelovich Date: Fri, 6 Dec 2024 10:56:31 -0500 Subject: [PATCH] fix: TypeError when listing data sets after opening one w/ encoding (#3348) * fix: call setEncoding after context value Signed-off-by: Trae Yelovich * tests: add test for setEncoding in ds constructor Signed-off-by: Trae Yelovich * move setEncoding call and fix tests Signed-off-by: Trae Yelovich * update changelog Signed-off-by: Trae Yelovich * remove unrelated comment from test block Signed-off-by: Trae Yelovich --------- Signed-off-by: Trae Yelovich --- packages/zowe-explorer/CHANGELOG.md | 1 + .../trees/dataset/DatasetTree.unit.test.ts | 11 ++++--- .../dataset/ZoweDatasetNode.unit.test.ts | 31 +++++++++++++++++-- .../trees/shared/SharedUtils.unit.test.ts | 2 ++ .../src/trees/dataset/ZoweDatasetNode.ts | 19 ++++++------ 5 files changed, 48 insertions(+), 16 deletions(-) diff --git a/packages/zowe-explorer/CHANGELOG.md b/packages/zowe-explorer/CHANGELOG.md index 4670c8aefa..8c37053005 100644 --- a/packages/zowe-explorer/CHANGELOG.md +++ b/packages/zowe-explorer/CHANGELOG.md @@ -28,6 +28,7 @@ All notable changes to the "vscode-extension-for-zowe" extension will be documen - Fixed an issue where a migrated data set is unusable after it is recalled through Zowe Explorer. [#3294](https://github.com/zowe/zowe-explorer-vscode/issues/3294) - Fixed an issue where a recalled PDS is expandable after it is migrated through Zowe Explorer. [#3294](https://github.com/zowe/zowe-explorer-vscode/issues/3294) - Fixed an issue where data set nodes did not update if migrated or recalled outside of Zowe Explorer. [#3294](https://github.com/zowe/zowe-explorer-vscode/issues/3294) +- Fixed an issue where listing data sets resulted in an error after opening a data set with an encoding. [#3347](https://github.com/zowe/zowe-explorer-vscode/issues/3347) ## `3.0.3` diff --git a/packages/zowe-explorer/__tests__/__unit__/trees/dataset/DatasetTree.unit.test.ts b/packages/zowe-explorer/__tests__/__unit__/trees/dataset/DatasetTree.unit.test.ts index 225736ef37..cb1aab8f98 100644 --- a/packages/zowe-explorer/__tests__/__unit__/trees/dataset/DatasetTree.unit.test.ts +++ b/packages/zowe-explorer/__tests__/__unit__/trees/dataset/DatasetTree.unit.test.ts @@ -3297,30 +3297,31 @@ describe("Dataset Tree Unit Tests - Sorting and Filtering operations", () => { describe("Dataset Tree Unit Tests - Function openWithEncoding", () => { it("sets binary encoding if selection was made", async () => { - const setEncodingMock = jest.spyOn(DatasetFSProvider.instance, "setEncodingForFile").mockImplementation(); const node = new ZoweDatasetNode({ label: "encodingTest", collapsibleState: vscode.TreeItemCollapsibleState.None }); + const setEncodingMock = jest.spyOn(node, "setEncoding").mockImplementation(); node.openDs = jest.fn(); jest.spyOn(SharedUtils, "promptForEncoding").mockResolvedValueOnce({ kind: "binary" }); await DatasetTree.prototype.openWithEncoding(node); - expect(setEncodingMock).toHaveBeenCalledWith(node.resourceUri, { kind: "binary" }); + expect(setEncodingMock).toHaveBeenCalledWith({ kind: "binary" }); expect(node.openDs).toHaveBeenCalledTimes(1); setEncodingMock.mockRestore(); }); it("sets text encoding if selection was made", async () => { - const setEncodingMock = jest.spyOn(DatasetFSProvider.instance, "setEncodingForFile").mockImplementation(); + jest.spyOn(DatasetFSProvider.instance, "exists").mockReturnValueOnce(true); const node = new ZoweDatasetNode({ label: "encodingTest", collapsibleState: vscode.TreeItemCollapsibleState.None }); + const setEncodingMock = jest.spyOn(node, "setEncoding").mockImplementation(); node.openDs = jest.fn(); jest.spyOn(SharedUtils, "promptForEncoding").mockResolvedValueOnce({ kind: "text" }); await DatasetTree.prototype.openWithEncoding(node); - expect(setEncodingMock).toHaveBeenCalledWith(node.resourceUri, { kind: "text" }); + expect(setEncodingMock).toHaveBeenCalledWith({ kind: "text" }); expect(node.openDs).toHaveBeenCalledTimes(1); setEncodingMock.mockRestore(); }); it("does not set encoding if prompt was cancelled", async () => { - const setEncodingSpy = jest.spyOn(DatasetFSProvider.instance, "setEncodingForFile"); const node = new ZoweDatasetNode({ label: "encodingTest", collapsibleState: vscode.TreeItemCollapsibleState.None }); + const setEncodingSpy = jest.spyOn(node, "setEncoding"); node.openDs = jest.fn(); jest.spyOn(SharedUtils, "promptForEncoding").mockResolvedValueOnce(undefined); await DatasetTree.prototype.openWithEncoding(node); diff --git a/packages/zowe-explorer/__tests__/__unit__/trees/dataset/ZoweDatasetNode.unit.test.ts b/packages/zowe-explorer/__tests__/__unit__/trees/dataset/ZoweDatasetNode.unit.test.ts index afcd8e8594..f16643eedc 100644 --- a/packages/zowe-explorer/__tests__/__unit__/trees/dataset/ZoweDatasetNode.unit.test.ts +++ b/packages/zowe-explorer/__tests__/__unit__/trees/dataset/ZoweDatasetNode.unit.test.ts @@ -10,7 +10,7 @@ */ import * as vscode from "vscode"; -import { DsEntry, Gui, imperative, PdsEntry, Validation, ZoweScheme } from "@zowe/zowe-explorer-api"; +import { BaseProvider, DsEntry, Gui, imperative, PdsEntry, Validation, ZoweScheme } from "@zowe/zowe-explorer-api"; import * as zosfiles from "@zowe/zos-files-for-zowe-sdk"; import { createSessCfgFromArgs, @@ -124,6 +124,26 @@ describe("ZoweDatasetNode Unit Tests", () => { expect(testNode.getSession()).toBeDefined(); }); + it("calls setEncoding when constructing a node with encoding", () => { + jest.spyOn(BaseProvider.prototype, "setEncodingForFile").mockImplementationOnce(() => {}); + const makeEmptyDsWithEncodingMock = jest.spyOn(DatasetFSProvider.instance, "makeEmptyDsWithEncoding").mockImplementationOnce(() => {}); + const setEncodingSpy = jest.spyOn(ZoweDatasetNode.prototype, "setEncoding"); + const testNode = new ZoweDatasetNode({ + label: "BRTVS99", + collapsibleState: vscode.TreeItemCollapsibleState.None, + contextOverride: Constants.DS_DS_CONTEXT, + encoding: { kind: "binary" }, + profile: createIProfile(), + parentNode: createDatasetSessionNode(createISession(), createIProfile()), + }); + + expect(testNode.label).toBe("BRTVS99"); + expect(testNode.collapsibleState).toBe(vscode.TreeItemCollapsibleState.None); + expect(setEncodingSpy).toHaveBeenCalled(); + expect(makeEmptyDsWithEncodingMock).toHaveBeenCalled(); + setEncodingSpy.mockRestore(); + }); + /************************************************************************************************************* * Checks that returning an unsuccessful response results in an error being thrown and caught *************************************************************************************************************/ @@ -635,9 +655,16 @@ describe("ZoweDatasetNode Unit Tests - Function node.openDs()", () => { }); describe("ZoweDatasetNode Unit Tests - Function node.setEncoding()", () => { - const setEncodingForFileMock = jest.spyOn(DatasetFSProvider.instance, "setEncodingForFile").mockImplementation(); + let setEncodingForFileMock: jest.SpyInstance; + let existsMock: jest.SpyInstance; + + beforeAll(() => { + setEncodingForFileMock = jest.spyOn(DatasetFSProvider.instance, "setEncodingForFile").mockImplementation(); + existsMock = jest.spyOn(DatasetFSProvider.instance, "exists").mockReturnValue(true); + }); afterAll(() => { + existsMock.mockRestore(); setEncodingForFileMock.mockRestore(); }); diff --git a/packages/zowe-explorer/__tests__/__unit__/trees/shared/SharedUtils.unit.test.ts b/packages/zowe-explorer/__tests__/__unit__/trees/shared/SharedUtils.unit.test.ts index f075fa22fd..8e0bbd2444 100644 --- a/packages/zowe-explorer/__tests__/__unit__/trees/shared/SharedUtils.unit.test.ts +++ b/packages/zowe-explorer/__tests__/__unit__/trees/shared/SharedUtils.unit.test.ts @@ -528,7 +528,9 @@ describe("Shared utils unit tests - function promptForEncoding", () => { parentNode, contextOverride: Constants.DS_MEMBER_CONTEXT, }); + const existsMock = jest.spyOn(DatasetFSProvider.instance, "exists").mockReturnValueOnce(true); node.setEncoding(otherEncoding); + expect(existsMock).toHaveBeenCalled(); blockMocks.getEncodingForFile.mockReturnValueOnce(undefined); await SharedUtils.promptForEncoding(node); expect(blockMocks.showQuickPick).toHaveBeenCalled(); diff --git a/packages/zowe-explorer/src/trees/dataset/ZoweDatasetNode.ts b/packages/zowe-explorer/src/trees/dataset/ZoweDatasetNode.ts index 70a47b01dc..cb6761e3d8 100644 --- a/packages/zowe-explorer/src/trees/dataset/ZoweDatasetNode.ts +++ b/packages/zowe-explorer/src/trees/dataset/ZoweDatasetNode.ts @@ -71,9 +71,6 @@ export class ZoweDatasetNode extends ZoweTreeNode implements IZoweDatasetTreeNod public constructor(opts: Definitions.IZoweDatasetTreeOpts) { super(opts.label, opts.collapsibleState, opts.parentNode, opts.session, opts.profile); - if (opts.encoding != null) { - this.setEncoding(opts.encoding); - } const isBinary = opts.encoding?.kind === "binary"; if (opts.contextOverride) { this.contextValue = opts.contextOverride; @@ -134,7 +131,7 @@ export class ZoweDatasetNode extends ZoweTreeNode implements IZoweDatasetTreeNod } if (opts.encoding != null) { - DatasetFSProvider.instance.makeEmptyDsWithEncoding(this.resourceUri, opts.encoding); + this.setEncoding(opts.encoding); } } } @@ -313,8 +310,8 @@ export class ZoweDatasetNode extends ZoweTreeNode implements IZoweDatasetTreeNod } else if (!SharedContext.isMigrated(dsNode) && item.migr?.toUpperCase() === "YES") { dsNode.datasetMigrated(); } - // Creates a ZoweDatasetNode for a PDS } else if (item.migr && item.migr.toUpperCase() === "YES") { + // Creates a ZoweDatasetNode for a migrated dataset dsNode = new ZoweDatasetNode({ label: item.dsname, collapsibleState: vscode.TreeItemCollapsibleState.None, @@ -323,8 +320,8 @@ export class ZoweDatasetNode extends ZoweTreeNode implements IZoweDatasetTreeNod profile: cachedProfile, }); elementChildren[dsNode.label.toString()] = dsNode; - // Creates a ZoweDatasetNode for a VSAM file } else if (item.dsorg === "PO" || item.dsorg === "PO-E") { + // Creates a ZoweDatasetNode for a PDS dsNode = new ZoweDatasetNode({ label: item.dsname, collapsibleState: vscode.TreeItemCollapsibleState.Collapsed, @@ -332,8 +329,8 @@ export class ZoweDatasetNode extends ZoweTreeNode implements IZoweDatasetTreeNod profile: cachedProfile, }); elementChildren[dsNode.label.toString()] = dsNode; - // Creates a ZoweDatasetNode for a dataset with imperative errors } else if (item.error instanceof imperative.ImperativeError) { + // Creates a ZoweDatasetNode for a dataset with imperative errors dsNode = new ZoweDatasetNode({ label: item.dsname, collapsibleState: vscode.TreeItemCollapsibleState.None, @@ -344,8 +341,8 @@ export class ZoweDatasetNode extends ZoweTreeNode implements IZoweDatasetTreeNod dsNode.command = { command: "zowe.placeholderCommand", title: "" }; dsNode.errorDetails = item.error; // Save imperative error to avoid extra z/OS requests elementChildren[dsNode.label.toString()] = dsNode; - // Creates a ZoweDatasetNode for a migrated dataset } else if (item.dsorg === "VS") { + // Creates a ZoweDatasetNode for a VSAM file let altLabel = item.dsname; let endPoint = altLabel.indexOf(".DATA"); if (endPoint === -1) { @@ -713,7 +710,11 @@ export class ZoweDatasetNode extends ZoweTreeNode implements IZoweDatasetTreeNod } else { this.contextValue = isMemberNode ? Constants.DS_MEMBER_CONTEXT : Constants.DS_DS_CONTEXT; } - DatasetFSProvider.instance.setEncodingForFile(this.resourceUri, encoding); + if (DatasetFSProvider.instance.exists(this.resourceUri)) { + DatasetFSProvider.instance.setEncodingForFile(this.resourceUri, encoding); + } else { + DatasetFSProvider.instance.makeEmptyDsWithEncoding(this.resourceUri, encoding); + } const fullPath = isMemberNode ? `${this.getParent().label as string}(${this.label as string})` : (this.label as string); if (encoding != null) { this.updateEncodingInMap(fullPath, encoding);