Skip to content

Commit

Permalink
Merge branch 'main' into fix/error-handling-saves
Browse files Browse the repository at this point in the history
Signed-off-by: Trae Yelovich <trae.yelovich@broadcom.com>
  • Loading branch information
traeok committed Oct 22, 2024
2 parents afba192 + e4a190e commit ab3ac95
Show file tree
Hide file tree
Showing 17 changed files with 200 additions and 60 deletions.
13 changes: 12 additions & 1 deletion .github/workflows/merge-by/build-table-and-notify.js
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,15 @@ const notifyUsers = async ({ dayJs, github, owner, pullRequests, repo, today })
});

for (const pr of prsCloseToMergeDate) {
const comments = (await github.rest.issues.listComments({ owner, repo, issue_number: pr.number })).data;
// Try to find the reminder comment
const existingComment = comments?.find((comment) =>
comment.user.login === "github-actions[bot]" && comment.body.includes("**Reminder:** This pull request has a merge-by date coming up within the next 24 hours. Please review this PR as soon as possible."));

if (existingComment != null) {
continue;
}

// Make a comment on the PR and tag reviewers
const body = `**Reminder:** This pull request has a merge-by date coming up within the next 24 hours. Please review this PR as soon as possible.\n\n${pr.reviewers.map((r) => `@${r.login}`).join(" ")}`
await github.rest.issues.createComment({
Expand Down Expand Up @@ -202,5 +211,7 @@ module.exports = async ({ github, context }) => {
// Look over existing PRs, grab all PRs with a merge-by date <= 1w from now, and update the issue with the new table
await scanPRsAndUpdateTable({ github, owner, pullRequests, repo });
// Notify users for PRs with merge-by dates coming up within 24hrs from now
await notifyUsers({ dayJs, github, owner, pullRequests, repo, today });
if (context.eventName === "schedule") {
await notifyUsers({ dayJs, github, owner, pullRequests, repo, today });
}
}
3 changes: 3 additions & 0 deletions packages/zowe-explorer/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ All notable changes to the "vscode-extension-for-zowe" extension will be documen

### Bug fixes

- `DatasetFSProvider.stat()` will now throw a `FileNotFound` error for extenders trying to fetch an MVS resource that does not exist. [#3252](https://github.com/zowe/zowe-explorer-vscode/issues/3252)
- Fixed an issue where renaming or deleting a USS file or data set did not update the opened editor. [#3260](https://github.com/zowe/zowe-explorer-vscode/issues/3260)

## `3.0.2`

### New features and enhancements
Expand Down
5 changes: 5 additions & 0 deletions packages/zowe-explorer/__tests__/__mocks__/vscode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1498,6 +1498,11 @@ export namespace workspace {
}
}

// We need to do this since "delete" is a reserved keyword and cannot be defined as a function name.
Object.defineProperty(workspace.fs, "delete", {
value: jest.fn(),
});

export interface InputBoxOptions {
placeholder?: string;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ function createGlobalMocks() {
testFavoritesNode: createDatasetFavoritesNode(),
testDatasetTree: null,
getContentsSpy: null,
fspDelete: jest.spyOn(DatasetFSProvider.prototype, "delete").mockImplementation(),
fspDelete: jest.spyOn(vscode.workspace.fs, "delete").mockImplementation(),
statusBarMsgSpy: null,
mvsApi: null,
mockShowWarningMessage: jest.fn(),
Expand Down Expand Up @@ -96,7 +96,6 @@ function createGlobalMocks() {
value: newMocks.mockShowWarningMessage,
configurable: true,
});
Object.defineProperty(vscode.workspace.fs, "delete", { value: jest.fn(), configurable: true });
Object.defineProperty(vscode.window, "showInputBox", { value: jest.fn(), configurable: true });
Object.defineProperty(vscode.workspace, "openTextDocument", { value: jest.fn(), configurable: true });
Object.defineProperty(vscode.workspace, "getConfiguration", { value: jest.fn(), configurable: true });
Expand Down Expand Up @@ -487,7 +486,7 @@ describe("Dataset Actions Unit Tests - Function deleteDatasetPrompt", () => {
blockMocks.testDatasetTree.getTreeView.mockReturnValueOnce(treeView);
globalMocks.mockShowWarningMessage.mockResolvedValueOnce("Delete");

jest.spyOn(DatasetFSProvider.instance, "delete").mockImplementation();
jest.spyOn(vscode.workspace.fs, "delete").mockImplementation();
await DatasetActions.deleteDatasetPrompt(blockMocks.testDatasetTree);

expect(mocked(Gui.showMessage)).toHaveBeenCalledWith(
Expand Down Expand Up @@ -721,7 +720,7 @@ describe("Dataset Actions Unit Tests - Function deleteDataset", () => {
});

mocked(vscode.window.showQuickPick).mockResolvedValueOnce("Delete" as any);
const deleteSpy = jest.spyOn(DatasetFSProvider.instance, "delete").mockImplementation();
const deleteSpy = jest.spyOn(vscode.workspace.fs, "delete").mockImplementation();
await DatasetActions.deleteDataset(node, blockMocks.testDatasetTree);
expect(deleteSpy).toHaveBeenCalledWith(node.resourceUri, { recursive: false });
});
Expand All @@ -737,7 +736,7 @@ describe("Dataset Actions Unit Tests - Function deleteDataset", () => {
});

mocked(vscode.window.showQuickPick).mockResolvedValueOnce("Delete" as any);
const deleteSpy = jest.spyOn(DatasetFSProvider.instance, "delete").mockImplementation();
const deleteSpy = jest.spyOn(vscode.workspace.fs, "delete").mockImplementation();
await DatasetActions.deleteDataset(node, blockMocks.testDatasetTree);
expect(deleteSpy).toHaveBeenCalledWith(node.resourceUri, { recursive: false });
});
Expand All @@ -753,7 +752,7 @@ describe("Dataset Actions Unit Tests - Function deleteDataset", () => {
});

mocked(vscode.window.showQuickPick).mockResolvedValueOnce("Delete" as any);
jest.spyOn(DatasetFSProvider.instance, "delete").mockRejectedValueOnce(Error("not found"));
jest.spyOn(vscode.workspace.fs, "delete").mockRejectedValueOnce(Error("not found"));
await expect(DatasetActions.deleteDataset(node, blockMocks.testDatasetTree)).rejects.toThrow("not found");
expect(mocked(Gui.showMessage)).toHaveBeenCalledWith("Unable to find file " + node.label?.toString());
});
Expand All @@ -769,7 +768,7 @@ describe("Dataset Actions Unit Tests - Function deleteDataset", () => {
});

mocked(vscode.window.showQuickPick).mockResolvedValueOnce("Delete" as any);
jest.spyOn(DatasetFSProvider.instance, "delete").mockRejectedValueOnce(Error("Deletion error"));
jest.spyOn(vscode.workspace.fs, "delete").mockRejectedValueOnce(Error("Deletion error"));
await expect(DatasetActions.deleteDataset(node, blockMocks.testDatasetTree)).rejects.toThrow("");
expect(mocked(Gui.errorMessage)).toHaveBeenCalledWith("Deletion error", { items: ["More info"] });
});
Expand All @@ -792,7 +791,7 @@ describe("Dataset Actions Unit Tests - Function deleteDataset", () => {
node.contextValue = Constants.DS_PDS_CONTEXT + Constants.FAV_SUFFIX;

mocked(vscode.window.showQuickPick).mockResolvedValueOnce("Delete" as any);
const deleteSpy = jest.spyOn(DatasetFSProvider.instance, "delete");
const deleteSpy = jest.spyOn(vscode.workspace.fs, "delete");

await DatasetActions.deleteDataset(node, blockMocks.testDatasetTree);

Expand All @@ -812,7 +811,7 @@ describe("Dataset Actions Unit Tests - Function deleteDataset", () => {
const child = new ZoweDatasetNode({ label: "child", collapsibleState: vscode.TreeItemCollapsibleState.None, parentNode: parent });

mocked(vscode.window.showQuickPick).mockResolvedValueOnce("Delete" as any);
const deleteSpy = jest.spyOn(DatasetFSProvider.instance, "delete").mockImplementation();
const deleteSpy = jest.spyOn(vscode.workspace.fs, "delete").mockImplementation();

await DatasetActions.deleteDataset(child, blockMocks.testDatasetTree);
expect(deleteSpy).toHaveBeenCalledWith(child.resourceUri, { recursive: false });
Expand Down Expand Up @@ -841,7 +840,7 @@ describe("Dataset Actions Unit Tests - Function deleteDataset", () => {
blockMocks.testDatasetTree.mFavorites[0].children.push(child);

mocked(vscode.window.showQuickPick).mockResolvedValueOnce("Delete" as any);
const deleteSpy = jest.spyOn(DatasetFSProvider.instance, "delete").mockImplementation();
const deleteSpy = jest.spyOn(vscode.workspace.fs, "delete").mockImplementation();
await DatasetActions.deleteDataset(child, blockMocks.testDatasetTree);
expect(deleteSpy).toHaveBeenCalledWith(child.resourceUri, { recursive: false });
expect(blockMocks.testDatasetTree.removeFavorite).toHaveBeenCalledWith(child);
Expand All @@ -864,7 +863,7 @@ describe("Dataset Actions Unit Tests - Function deleteDataset", () => {
});

mocked(vscode.window.showQuickPick).mockResolvedValueOnce("Delete" as any);
const deleteSpy = jest.spyOn(DatasetFSProvider.instance, "delete").mockImplementation();
const deleteSpy = jest.spyOn(vscode.workspace.fs, "delete").mockImplementation();
deleteSpy.mockClear();
await expect(DatasetActions.deleteDataset(child, blockMocks.testDatasetTree)).rejects.toThrow("Cannot delete, item invalid.");
expect(deleteSpy).not.toHaveBeenCalled();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -862,6 +862,33 @@ describe("fetchEntriesForProfile", () => {
describe("fetchDataset", () => {
describe("calls dataSet to verify that the data set exists on the mainframe", () => {
describe("PS", () => {
it("non-existent PS URI", async () => {
const dataSetMock = jest.fn().mockResolvedValue({
success: true,
apiResponse: {
items: [],
},
commandResponse: "",
});
const mvsApiMock = jest.spyOn(ZoweExplorerApiRegister, "getMvsApi").mockReturnValue({
dataSet: dataSetMock,
} as any);
try {
await (DatasetFSProvider.instance as any).fetchDataset(testUris.ps, {
isRoot: false,
slashAfterProfilePos: testUris.ps.path.indexOf("/", 1),
profileName: "sestest",
profile: testProfile,
});
// Fail test if above expression doesn't throw anything.
expect(true).toBe(false);
} catch (e) {
expect(e.message).toBe(testUris.ps.toString(true));
}
expect(dataSetMock).toHaveBeenCalled();
mvsApiMock.mockRestore();
});

it("non-existent URI", async () => {
const dataSetMock = jest.fn().mockResolvedValue({
success: true,
Expand All @@ -875,7 +902,7 @@ describe("fetchDataset", () => {
} as any);
await (DatasetFSProvider.instance as any).fetchDataset(testUris.ps, {
isRoot: false,
slashAfterProfilePos: testUris.pds.path.indexOf("/", 1),
slashAfterProfilePos: testUris.ps.path.indexOf("/", 1),
profileName: "sestest",
profile: testProfile,
});
Expand All @@ -900,6 +927,36 @@ describe("fetchDataset", () => {
});

describe("PDS", () => {
it("non-existent PDS URI", async () => {
const dataSetMock = jest.fn().mockResolvedValue({
success: true,
apiResponse: {
items: [],
},
commandResponse: "",
});
const mvsApiMock = jest.spyOn(ZoweExplorerApiRegister, "getMvsApi").mockReturnValue({
dataSet: dataSetMock,
} as any);
const lookupMock = jest.spyOn(DatasetFSProvider.instance as any, "lookup");
lookupMock.mockImplementation(() => {
throw FileSystemError.FileNotFound(testUris.pds);
});
try {
await (DatasetFSProvider.instance as any).fetchDataset(testUris.pds, {
isRoot: false,
slashAfterProfilePos: testUris.pds.path.indexOf("/", 1),
profileName: "sestest",
profile: testProfile,
});
// Fail test if above expression doesn't throw anything.
expect(true).toBe(false);
} catch (e) {
expect(e.message).toBe(testUris.pds.toString(true));
}
lookupMock.mockRestore();
mvsApiMock.mockRestore();
});
it("non-existent URI", async () => {
const dataSetMock = jest.fn().mockResolvedValue({
success: true,
Expand Down Expand Up @@ -935,6 +992,63 @@ describe("fetchDataset", () => {
fetchEntriesForDatasetMock.mockRestore();
});
});

describe("PDS member", () => {
it("non-existent member URI", async () => {
const allMembersMockNoMatch = jest.fn().mockResolvedValue({
success: true,
apiResponse: {
items: [
{
member: "NOMATCH",
},
],
},
commandResponse: "",
});
const mvsApiMock = jest.spyOn(ZoweExplorerApiRegister, "getMvsApi").mockReturnValue({
allMembers: allMembersMockNoMatch,
} as any);
try {
await (DatasetFSProvider.instance as any).fetchDataset(testUris.pdsMember, {
isRoot: false,
slashAfterProfilePos: testUris.pds.path.indexOf("/", 1),
profileName: "sestest",
profile: testProfile,
});
// Fail test if above expression doesn't throw anything.
expect(true).toBe(false);
} catch (e) {
expect(e.message).toBe(testUris.pdsMember.toString(true));
}
expect(allMembersMockNoMatch).toHaveBeenCalledWith("USER.DATA.PDS");
mvsApiMock.mockRestore();
});
it("existing member URI", async () => {
const allMembersMock = jest.fn().mockResolvedValue({
success: true,
apiResponse: {
items: [
{
member: "MEMBER1",
},
],
},
commandResponse: "",
});
const mvsApiMock = jest.spyOn(ZoweExplorerApiRegister, "getMvsApi").mockReturnValue({
allMembers: allMembersMock,
} as any);
await (DatasetFSProvider.instance as any).fetchDataset(testUris.pdsMember, {
isRoot: false,
slashAfterProfilePos: testUris.pdsMember.path.indexOf("/", 1),
profileName: "sestest",
profile: testProfile,
});
expect(allMembersMock).toHaveBeenCalledWith("USER.DATA.PDS");
mvsApiMock.mockRestore();
});
});
});
it("calls _handleError whenever an unknown filesystem error occurs", async () => {
const lookupMock = jest.spyOn(DatasetFSProvider.instance, "lookup").mockImplementation(() => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2234,7 +2234,7 @@ describe("Dataset Tree Unit Tests - Function rename", () => {
mvsApi,
profileInstance,
mockCheckCurrentProfile,
rename: jest.spyOn(DatasetFSProvider.instance, "rename").mockImplementation(),
rename: jest.spyOn(vscode.workspace.fs, "rename").mockImplementation(),
};
}

Expand Down Expand Up @@ -2481,7 +2481,7 @@ describe("Dataset Tree Unit Tests - Function rename", () => {
favProfileNode.children.push(favParent);
testTree.mFavorites.push(favProfileNode);
const renameDataSetMemberSpy = jest.spyOn((DatasetTree as any).prototype, "renameDataSetMember");
const renameMock = jest.spyOn(DatasetFSProvider.instance, "rename").mockImplementation();
const renameMock = jest.spyOn(vscode.workspace.fs, "rename").mockImplementation();

await testTree.rename(child);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -336,7 +336,7 @@ describe("delete", () => {
const lookupParentDirMock = jest
.spyOn(JobFSProvider.instance as any, "_lookupParentDirectory")
.mockReturnValueOnce({ ...testEntries.session });
await JobFSProvider.instance.delete(testUris.job, { recursive: true, deleteRemote: true });
await JobFSProvider.instance.delete(testUris.job, { recursive: true });
const jobInfo = testEntries.job.job;
expect(jobInfo).not.toBeUndefined();
expect(mockUssApi.deleteJob).toHaveBeenCalledWith(jobInfo?.jobname || "TESTJOB", jobInfo?.jobid || "JOB12345");
Expand Down Expand Up @@ -375,7 +375,7 @@ describe("delete", () => {
fakeJob.job = testEntries.job.job;
const lookupMock = jest.spyOn(JobFSProvider.instance as any, "lookup").mockReturnValueOnce(fakeSpool);
const lookupParentDirMock = jest.spyOn(JobFSProvider.instance as any, "_lookupParentDirectory").mockReturnValueOnce(fakeJob);
await JobFSProvider.instance.delete(testUris.spool, { recursive: true, deleteRemote: true });
await JobFSProvider.instance.delete(testUris.spool, { recursive: true });
expect(mockUssApi.deleteJob).not.toHaveBeenCalled();
expect(lookupParentDirMock).not.toHaveBeenCalled();
ussApiMock.mockRestore();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ async function createGlobalMocks() {
};

jest.spyOn(JobFSProvider.instance, "createDirectory").mockImplementation(globalMocks.FileSystemProvider.createDirectory);
jest.spyOn(JobFSProvider.instance, "delete").mockImplementation(globalMocks.FileSystemProvider.delete);
jest.spyOn(vscode.workspace.fs, "delete").mockImplementation(globalMocks.FileSystemProvider.delete);
jest.spyOn(Gui, "createTreeView").mockImplementation(globalMocks.createTreeView);
Object.defineProperty(ProfilesCache, "getConfigInstance", {
value: jest.fn(() => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ function createGlobalMocks() {
};

jest.spyOn(UssFSProvider.instance, "createDirectory").mockImplementation(globalMocks.FileSystemProvider.createDirectory);
jest.spyOn(UssFSProvider.instance, "rename").mockImplementation(globalMocks.FileSystemProvider.rename);
jest.spyOn(vscode.workspace.fs, "rename").mockImplementation(globalMocks.FileSystemProvider.rename);

globalMocks.mockTextDocuments.push(globalMocks.mockTextDocumentDirty);
globalMocks.mockTextDocuments.push(globalMocks.mockTextDocumentClean);
Expand Down Expand Up @@ -1690,7 +1690,7 @@ describe("USSTree Unit Tests - Function crossLparMove", () => {
];
ussDirNode.dirty = false;

const deleteMock = jest.spyOn(UssFSProvider.instance, "delete").mockResolvedValue(undefined);
const deleteMock = jest.spyOn(vscode.workspace.fs, "delete").mockResolvedValue(undefined);
const readFileMock = jest.spyOn(UssFSProvider.instance, "readFile").mockResolvedValue(new Uint8Array([1, 2, 3]));
const writeFileMock = jest.spyOn(UssFSProvider.instance, "writeFile").mockResolvedValue(undefined);
const existsMock = jest.spyOn(UssFSProvider.instance, "exists").mockReturnValueOnce(false);
Expand Down
Loading

0 comments on commit ab3ac95

Please sign in to comment.