Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ds: Show error for unsaved dataset rename; use mtime in ms for DS entries #3326

Merged
merged 16 commits into from
Nov 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion packages/zowe-explorer/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@ All notable changes to the "vscode-extension-for-zowe" extension will be documen

### New features and enhancements

- Updated Zowe SDKs to `8.8.2` for technical currency. [#3296](https://github.com/zowe/zowe-explorer-vscode/pull/3296)
- Added expired JSON web token detection for profiles in each tree view (Data Sets, USS, Jobs). When a user performs a search on a profile, they are prompted to log in if their token expired. [#3175](https://github.com/zowe/zowe-explorer-vscode/issues/3175)
- Add a data set or USS resource to a virtual workspace with the new "Add to Workspace" context menu option. [#3265](https://github.com/zowe/zowe-explorer-vscode/issues/3265)
- Power users and developers can now build links to efficiently open mainframe resources in Zowe Explorer. Use the **Copy External Link** option in the context menu to get the URL for a data set or USS resource, or create a link in the format `vscode://Zowe.vscode-extension-for-zowe?<ZoweResourceUri>`. For more information on building resource URIs, see the [FileSystemProvider wiki article](https://github.com/zowe/zowe-explorer-vscode/wiki/FileSystemProvider#file-paths-vs-uris). [#3271](https://github.com/zowe/zowe-explorer-vscode/pull/3271)
- Implemented more user-friendly error messages for API or network errors within Zowe Explorer. [#3243](https://github.com/zowe/zowe-explorer-vscode/pull/3243)
- Use the "Troubleshoot" option for certain errors to obtain additional context, tips, and resources for how to resolve the errors. [#3243](https://github.com/zowe/zowe-explorer-vscode/pull/3243)
- Updated Zowe SDKs to `8.8.2` for technical currency. [#3296](https://github.com/zowe/zowe-explorer-vscode/pull/3296)
- Allow extenders to add context menu actions to a top level node, i.e. data sets, USS, Jobs, by encoding the profile type in the context value. [#3309](https://github.com/zowe/zowe-explorer-vscode/pull/3309)
- You can now add multiple partitioned data sets or USS directories to your workspace at once using the "Add to Workspace" feature. [#3324](https://github.com/zowe/zowe-explorer-vscode/issues/3324)
- Exposed read and write access to local storage keys for Zowe Explorer extenders. [#3180](https://github.com/zowe/zowe-explorer-vscode/issues/3180)
Expand All @@ -22,6 +22,7 @@ All notable changes to the "vscode-extension-for-zowe" extension will be documen
- Fixed an issue where editing a team config file or updating secrets in the OS credential vault could trigger multiple events for a single action. [#3296](https://github.com/zowe/zowe-explorer-vscode/pull/3296)
- Fixed an issue where opening a PDS member after renaming an expanded PDS resulted in an error. [#3314](https://github.com/zowe/zowe-explorer-vscode/issues/3314)
- Fixed issue where persistent settings defined at the workspace level were migrated into global storage rather than workspace-specific storage. [#3180](https://github.com/zowe/zowe-explorer-vscode/issues/3180)
- Fixed an issue where renaming a data set with unsaved changes did not cancel the rename operation. Now, when renaming a data set with unsaved changes, you are prompted to resolve them before continuing. [#3326](https://github.com/zowe/zowe-explorer-vscode/pull/3326)

## `3.0.3`

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,6 @@ export function createUSSSessionNode(session: imperative.Session, profile: imper
collapsibleState: vscode.TreeItemCollapsibleState.Collapsed,
session,
profile,
parentPath: "/",
});
zoweUSSNode.fullPath = "/test";
zoweUSSNode.contextValue = Constants.USS_SESSION_CONTEXT;
Expand Down
3 changes: 1 addition & 2 deletions packages/zowe-explorer/__tests__/__mocks__/vscode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -918,7 +918,7 @@ export namespace l10n {
return options;
}
options.args?.forEach((arg: string, i: number) => {
options.message = options.message.replace(`{${i}}`, arg);
options.message = options.message.replaceAll(`{${i}}`, arg);
});
return options.message;
}
Expand Down Expand Up @@ -1312,7 +1312,6 @@ export enum FileSystemProviderErrorCode {
* a file or folder doesn't exist, use them like so: `throw vscode.FileSystemError.FileNotFound(someUri);`
*/
export const { FileSystemError } = require("jest-mock-vscode").createVSCodeMock(jest);

/**
* Namespace for dealing with the current workspace. A workspace is the representation
* of the folder that has been opened. There is no workspace when just a file but not a
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -703,7 +703,7 @@ describe("stat", () => {
expect(lookupMock).toHaveBeenCalledWith(testUris.pdsMember, false);
expect(lookupParentDirMock).toHaveBeenCalledWith(testUris.pdsMember);
expect(allMembersMock).toHaveBeenCalledWith("USER.DATA.PDS", { attributes: true });
expect(res).toStrictEqual({ ...fakePdsMember, mtime: dayjs("2024-08-08 12:30").unix() });
expect(res).toStrictEqual({ ...fakePdsMember, mtime: dayjs("2024-08-08 12:30").valueOf() });
expect(fakePdsMember.wasAccessed).toBe(false);
lookupMock.mockRestore();
lookupParentDirMock.mockRestore();
Expand Down Expand Up @@ -1126,6 +1126,7 @@ describe("rename", () => {
.mockImplementation((uri): DirEntry | FileEntry => ((uri as Uri).path.includes("USER.DATA.PS2") ? (null as any) : oldPs));
const _lookupParentDirectoryMock = jest
.spyOn(DatasetFSProvider.instance as any, "_lookupParentDirectory")
.mockReturnValueOnce({ ...testEntries.session })
.mockReturnValueOnce({ ...testEntries.session });
await DatasetFSProvider.instance.rename(testUris.ps, testUris.ps.with({ path: "/USER.DATA.PS2" }), { overwrite: true });
expect(mockMvsApi.renameDataSet).toHaveBeenCalledWith("USER.DATA.PS", "USER.DATA.PS2");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ import { Sorting } from "../../../../../zowe-explorer-api/src/tree";
import { IconUtils } from "../../../../src/icons/IconUtils";
import { SharedContext } from "../../../../src/trees/shared/SharedContext";
import { ZoweTreeProvider } from "../../../../src/trees/ZoweTreeProvider";
import { TreeViewUtils } from "../../../../src/utils/TreeViewUtils";

jest.mock("fs");
jest.mock("util");
Expand Down Expand Up @@ -2280,6 +2281,31 @@ describe("Dataset Tree Unit Tests - Function rename", () => {
};
}

it("returns early if errorForUnsavedResource was true", async () => {
createGlobalMocks();
const blockMocks = createBlockMocks();
mocked(Profiles.getInstance).mockReturnValue(blockMocks.profileInstance);
mocked(vscode.window.createTreeView).mockReturnValueOnce(blockMocks.treeView);
const testTree = new DatasetTree();
testTree.mSessionNodes.push(blockMocks.datasetSessionNode);
const node = new ZoweDatasetNode({
label: "HLQ.TEST.RENAME.NODE",
collapsibleState: vscode.TreeItemCollapsibleState.None,
parentNode: testTree.mSessionNodes[1],
session: blockMocks.session,
profile: testTree.mSessionNodes[1].getProfile(),
});
blockMocks.rename.mockClear();
const errorForUnsavedResource = jest.spyOn(TreeViewUtils, "errorForUnsavedResource").mockResolvedValueOnce(true);
await testTree.rename(node);
expect(errorForUnsavedResource).toHaveBeenCalled();
expect(blockMocks.rename).not.toHaveBeenLastCalledWith(
{ path: "/sestest/HLQ.TEST.RENAME.NODE", scheme: ZoweScheme.DS },
{ path: "/sestest/HLQ.TEST.RENAME.NODE.NEW", scheme: ZoweScheme.DS },
{ overwrite: false }
);
});

it("Tests that rename() renames a node", async () => {
createGlobalMocks();
const blockMocks = createBlockMocks();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ import { FilterDescriptor } from "../../../../src/management/FilterManagement";
import { AuthUtils } from "../../../../src/utils/AuthUtils";
import { Icon } from "../../../../src/icons/Icon";
import { ZoweTreeProvider } from "../../../../src/trees/ZoweTreeProvider";
import { TreeViewUtils } from "../../../../src/utils/TreeViewUtils";
import { SharedContext } from "../../../../src/trees/shared/SharedContext";

function createGlobalMocks() {
const globalMocks = {
Expand Down Expand Up @@ -934,6 +936,7 @@ describe("USSTree Unit Tests - Function rename", () => {
globalMocks.FileSystemProvider.rename.mockClear();

const newMocks = {
errorForUnsavedResource: jest.spyOn(TreeViewUtils, "errorForUnsavedResource").mockResolvedValueOnce(false),
ussFavNode,
ussFavNodeParent,
setAttributes: jest.spyOn(ZoweUSSNode.prototype, "setAttributes").mockImplementation(),
Expand All @@ -948,6 +951,23 @@ describe("USSTree Unit Tests - Function rename", () => {
getEncodingForFileMock.mockRestore();
});

it("returns early if errorForUnsavedResource was true", async () => {
const globalMocks = createGlobalMocks();
const blockMocks = createBlockMocks(globalMocks);
blockMocks.errorForUnsavedResource.mockReset();
blockMocks.errorForUnsavedResource.mockResolvedValueOnce(true);
const testUSSDir = new ZoweUSSNode({
label: "test",
collapsibleState: vscode.TreeItemCollapsibleState.Expanded,
session: globalMocks.testSession,
profile: globalMocks.testProfile,
parentPath: "/",
});
const isFolderMock = jest.spyOn(SharedContext, "isFolder");
await globalMocks.testTree.rename(testUSSDir);
expect(isFolderMock).not.toHaveBeenCalled();
});

it("Tests that USSTree.rename() shows no error if an open dirty file's fullpath includes that of the node being renamed", async () => {
// Open dirty file defined by globalMocks.mockTextDocumentDirty, with filepath including "sestest/test/node"
const globalMocks = createGlobalMocks();
Expand Down Expand Up @@ -1233,7 +1253,6 @@ describe("USSTree Unit Tests - Function getChildren", () => {
contextOverride: Constants.USS_SESSION_CONTEXT,
session: globalMocks.testSession,
profile: globalMocks.testProfile,
parentPath: "/",
}),
];

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,26 @@ describe("stat", () => {
expect(listFilesMock).toHaveBeenCalled();
listFilesMock.mockRestore();
});

it("updates a file entry with new modification time and resets wasAccessed flag", async () => {
const fakeFile = Object.assign(Object.create(Object.getPrototypeOf(testEntries.file)), testEntries.file);
lookupMock.mockReturnValueOnce(fakeFile);
const newMtime = Date.now();
const listFilesMock = jest.spyOn(UssFSProvider.instance, "listFiles").mockResolvedValueOnce({
success: true,
apiResponse: {
items: [{ name: fakeFile.name, mtime: newMtime }],
},
commandResponse: "",
});
await expect(UssFSProvider.instance.stat(testUris.file)).resolves.toStrictEqual(fakeFile);
expect(lookupMock).toHaveBeenCalledWith(testUris.file, false);
expect(fakeFile.mtime).toBe(newMtime);
expect(fakeFile.wasAccessed).toBe(false);
expect(listFilesMock).toHaveBeenCalled();
listFilesMock.mockRestore();
});

it("returns a file as 'read-only' when query has conflict parameter", async () => {
lookupMock.mockReturnValueOnce(testEntries.file);
await expect(UssFSProvider.instance.stat(testUris.conflictFile)).resolves.toStrictEqual({
Expand Down
Loading
Loading