diff --git a/packages/zowe-explorer/CHANGELOG.md b/packages/zowe-explorer/CHANGELOG.md index 0ea1ffad1e..cf76e5b28e 100644 --- a/packages/zowe-explorer/CHANGELOG.md +++ b/packages/zowe-explorer/CHANGELOG.md @@ -10,6 +10,7 @@ All notable changes to the "vscode-extension-for-zowe" extension will be documen - Introduce a new user interface for managing profiles via right-click action "Manage Profile". - Added new edit feature on `Edit Attributes` view for changing file tags on USS [#2113](https://github.com/zowe/vscode-extension-for-zowe/issues/2113) - Added new API {ZE Extender MetaData} to allow extenders to have the metadata of registered extenders to aid in team configuration file creation from a view that isn't Zowe Explorer's. [#2394](https://github.com/zowe/vscode-extension-for-zowe/issues/2394) +- Added new right-click action for `Submit as JCL` for local files in the VS Code file explorer as well as files opened in the VS Code text editor. [#2475](https://github.com/zowe/vscode-extension-for-zowe/issues/2475) - Added "Sort PDS members" feature in Data Sets tree view: accessible via sort icon on session node, or by right-clicking a PDS or session. [#2420](https://github.com/zowe/vscode-extension-for-zowe/issues/2420) - Added "Filter PDS members" feature in Data Sets tree view: accessible via filter icon on session node, or by right-clicking a PDS or session. [#2420](https://github.com/zowe/vscode-extension-for-zowe/issues/2420) - Added descriptions to data set nodes if filtering and/or sorting is enabled (where applicable). @@ -17,7 +18,7 @@ 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 submitting local JCL using command pallet option `Zowe Explorer: Submit as 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 conflict resolution being skipped if local and remote file have different contents but are the same size. [#2496](https://github.com/zowe/vscode-extension-for-zowe/issues/2496) - Fixed issue with token based auth for unsecure profiles in Zowe Explorer [#2518](https://github.com/zowe/vscode-extension-for-zowe/issues/2518) diff --git a/packages/zowe-explorer/__tests__/__unit__/dataset/init.unit.test.ts b/packages/zowe-explorer/__tests__/__unit__/dataset/init.unit.test.ts index 2697d100c1..443446b047 100644 --- a/packages/zowe-explorer/__tests__/__unit__/dataset/init.unit.test.ts +++ b/packages/zowe-explorer/__tests__/__unit__/dataset/init.unit.test.ts @@ -158,7 +158,7 @@ describe("Test src/dataset/extension", () => { }, { name: "zowe.ds.submitJcl", - mock: [{ spy: jest.spyOn(dsActions, "submitJcl"), arg: [dsProvider] }], + mock: [{ spy: jest.spyOn(dsActions, "submitJcl"), arg: [dsProvider, test.value] }], }, { name: "zowe.ds.submitMember", 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 528a5f01b0..dd91a263b2 100644 --- a/packages/zowe-explorer/__tests__/__unit__/job/actions.unit.test.ts +++ b/packages/zowe-explorer/__tests__/__unit__/job/actions.unit.test.ts @@ -46,6 +46,43 @@ import { ZosJobsProvider } from "../../../src/job/ZosJobsProvider"; const activeTextEditorDocument = jest.fn(); function createGlobalMocks() { + const newMocks = { + session: createISession(), + treeView: createTreeView(), + iJob: createIJobObject(), + imperativeProfile: createIProfile(), + JobNode1: new Job( + "testProfile", + vscode.TreeItemCollapsibleState.None, + null as any, + createISession(), + settingJobObjects(createIJobObject(), "ZOWEUSR1", "JOB045123", "ABEND S222"), + createIProfile() + ), + JobNode2: new Job( + "testProfile", + vscode.TreeItemCollapsibleState.None, + null as any, + createISession(), + settingJobObjects(createIJobObject(), "ZOWEUSR1", "JOB045120", "CC 0000"), + createIProfile() + ), + JobNode3: new Job( + "testProfile", + vscode.TreeItemCollapsibleState.None, + null as any, + createISession(), + settingJobObjects(createIJobObject(), "ZOWEUSR2", "JOB045125", "CC 0000"), + createIProfile() + ), + mockJobArray: [], + testJobsTree: null as any, + jesApi: null as any, + }; + newMocks.testJobsTree = createJobsTree(newMocks.session, newMocks.iJob, newMocks.imperativeProfile, newMocks.treeView); + newMocks.mockJobArray = [newMocks.JobNode1, newMocks.JobNode2, newMocks.JobNode3] as any; + newMocks.jesApi = createJesApi(newMocks.imperativeProfile); + bindJesApi(newMocks.jesApi); 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]])), @@ -94,41 +131,7 @@ function createGlobalMocks() { job.retcode = setjobreturncode; return job; } - const newMocks = jest.fn().mockReturnValue([ - (() => { - const JobNode1 = new Job( - "testProfile", - vscode.TreeItemCollapsibleState.None, - null, - createISession(), - settingJobObjects(createIJobObject(), "ZOWEUSR1", "JOB045123", "ABEND S222"), - createIProfile() - ); - return JobNode1; - })(), - (() => { - const JobNode2 = new Job( - "testProfile", - vscode.TreeItemCollapsibleState.None, - null, - createISession(), - settingJobObjects(createIJobObject(), "ZOWEUSR1", "JOB045120", "CC 0000"), - createIProfile() - ); - return JobNode2; - })(), - (() => { - const JobNode3 = new Job( - "testProfile", - vscode.TreeItemCollapsibleState.None, - null, - createISession(), - settingJobObjects(createIJobObject(), "ZOWEUSR2", "JOB045125", "CC 0000"), - createIProfile() - ); - return JobNode3; - })(), - ]); + return newMocks; } @@ -140,25 +143,9 @@ afterEach(() => { }); describe("Jobs Actions Unit Tests - Function setPrefix", () => { - function createBlockMocks() { - const session = createISession(); - const treeView = createTreeView(); - const iJob = createIJobObject(); - const imperativeProfile = createIProfile(); - - return { - session, - treeView, - iJob, - imperativeProfile, - testJobsTree: createJobsTree(session, iJob, imperativeProfile, treeView), - }; - } - it("Checking that the prefix is set correctly on the job", async () => { - createGlobalMocks(); - const blockMocks = createBlockMocks(); - const node = new Job("job", vscode.TreeItemCollapsibleState.None, null, blockMocks.session, null, null); + const blockMocks = createGlobalMocks(); + const node = new Job("job", vscode.TreeItemCollapsibleState.None, null as any, blockMocks.session, null as any, null as any); const mySpy = mocked(vscode.window.showInputBox).mockResolvedValue("*"); await jobActions.setPrefix(node, blockMocks.testJobsTree); @@ -175,25 +162,16 @@ describe("Jobs Actions Unit Tests - Function setPrefix", () => { }); describe("Jobs Actions Unit Tests - Function setOwner", () => { - function createBlockMocks() { - const session = createISession(); - const treeView = createTreeView(); - const iJob = createIJobObject(); - const imperativeProfile = createIProfile(); - - return { - session, - treeView, - iJob, - imperativeProfile, - testJobsTree: createJobsTree(session, iJob, imperativeProfile, treeView), - }; - } - it("Checking that the owner is set correctly on the job", async () => { - createGlobalMocks(); - const blockMocks = createBlockMocks(); - const node = new Job("job", vscode.TreeItemCollapsibleState.None, null, blockMocks.session, blockMocks.iJob, blockMocks.imperativeProfile); + const blockMocks = createGlobalMocks(); + const node = new Job( + "job", + vscode.TreeItemCollapsibleState.None, + null as any, + blockMocks.session, + blockMocks.iJob, + blockMocks.imperativeProfile + ); const mySpy = mocked(vscode.window.showInputBox).mockResolvedValue("OWNER"); await jobActions.setOwner(node, blockMocks.testJobsTree); @@ -210,22 +188,16 @@ describe("Jobs Actions Unit Tests - Function setOwner", () => { }); describe("Jobs Actions Unit Tests - Function stopCommand", () => { - function createBlockMocks() { - const session = createISession(); - const iJob = createIJobObject(); - const imperativeProfile = createIProfile(); - - return { - session, - iJob, - imperativeProfile, - }; - } - it("Checking that stop command of Job Node is executed properly", async () => { - createGlobalMocks(); - const blockMocks = createBlockMocks(); - const node = new Job("job", vscode.TreeItemCollapsibleState.None, null, blockMocks.session, blockMocks.iJob, blockMocks.imperativeProfile); + const blockMocks = createGlobalMocks(); + const node = new Job( + "job", + vscode.TreeItemCollapsibleState.None, + null as any, + blockMocks.session, + blockMocks.iJob, + blockMocks.imperativeProfile + ); mocked(zowe.IssueCommand.issueSimple).mockResolvedValueOnce({ success: false, @@ -237,9 +209,15 @@ describe("Jobs Actions Unit Tests - Function stopCommand", () => { expect(mocked(Gui.showMessage).mock.calls[0][0]).toEqual("Command response: fake response"); }); it("Checking failed attempt to issue stop command for Job Node.", async () => { - createGlobalMocks(); - const blockMocks = createBlockMocks(); - const node = new Job("job", vscode.TreeItemCollapsibleState.None, null, blockMocks.session, undefined, blockMocks.imperativeProfile); + const blockMocks = createGlobalMocks(); + const node = new Job( + "job", + vscode.TreeItemCollapsibleState.None, + null as any, + blockMocks.session, + undefined as any, + blockMocks.imperativeProfile + ); mocked(zowe.IssueCommand.issueSimple).mockResolvedValueOnce({ success: false, zosmfResponse: [], @@ -251,22 +229,16 @@ describe("Jobs Actions Unit Tests - Function stopCommand", () => { }); describe("Jobs Actions Unit Tests - Function modifyCommand", () => { - function createBlockMocks() { - const session = createISession(); - const iJob = createIJobObject(); - const imperativeProfile = createIProfile(); - - return { - session, - iJob, - imperativeProfile, - }; - } - it("Checking modification of Job Node", async () => { - createGlobalMocks(); - const blockMocks = createBlockMocks(); - const node = new Job("job", vscode.TreeItemCollapsibleState.None, null, blockMocks.session, blockMocks.iJob, blockMocks.imperativeProfile); + const blockMocks = createGlobalMocks(); + const node = new Job( + "job", + vscode.TreeItemCollapsibleState.None, + null as any, + blockMocks.session, + blockMocks.iJob, + blockMocks.imperativeProfile + ); mocked(vscode.window.showInputBox).mockResolvedValue("modify"); mocked(zowe.IssueCommand.issueSimple).mockResolvedValueOnce({ @@ -279,9 +251,15 @@ describe("Jobs Actions Unit Tests - Function modifyCommand", () => { expect(mocked(Gui.showMessage).mock.calls[0][0]).toEqual("Command response: fake response"); }); it("Checking failed attempt to modify Job Node", async () => { - createGlobalMocks(); - const blockMocks = createBlockMocks(); - const node = new Job("job", vscode.TreeItemCollapsibleState.None, null, blockMocks.session, undefined, blockMocks.imperativeProfile); + const blockMocks = createGlobalMocks(); + const node = new Job( + "job", + vscode.TreeItemCollapsibleState.None, + null as any, + blockMocks.session, + undefined as any, + blockMocks.imperativeProfile + ); mocked(vscode.window.showInputBox).mockResolvedValue("modify"); mocked(zowe.IssueCommand.issueSimple).mockResolvedValueOnce({ success: false, @@ -294,26 +272,17 @@ describe("Jobs Actions Unit Tests - Function modifyCommand", () => { }); describe("Jobs Actions Unit Tests - Function downloadSpool", () => { - function createBlockMocks() { - const session = createISession(); - const iJob = createIJobObject(); - const imperativeProfile = createIProfile(); - const jesApi = createJesApi(imperativeProfile); - bindJesApi(jesApi); - - return { - session, - iJob, - imperativeProfile, - jesApi, - }; - } - it("Checking download of Job Spool", async () => { - createGlobalMocks(); - const blockMocks = createBlockMocks(); + const blockMocks = createGlobalMocks(); const jobs: Job[] = []; - const node = new Job("job", vscode.TreeItemCollapsibleState.None, null, blockMocks.session, blockMocks.iJob, blockMocks.imperativeProfile); + const node = new Job( + "job", + vscode.TreeItemCollapsibleState.None, + null as any, + blockMocks.session, + blockMocks.iJob, + blockMocks.imperativeProfile + ); const fileUri = { fsPath: "/tmp/foo", scheme: "", @@ -337,7 +306,6 @@ describe("Jobs Actions Unit Tests - Function downloadSpool", () => { }); it("Checking failed attempt to download Job Spool", async () => { createGlobalMocks(); - const blockMocks = createBlockMocks(); const fileUri = { fsPath: "/tmp/foo", scheme: "", @@ -347,36 +315,20 @@ describe("Jobs Actions Unit Tests - Function downloadSpool", () => { query: "", }; mocked(Gui.showOpenDialog).mockResolvedValue([fileUri as vscode.Uri]); - await jobActions.downloadSpool(undefined); + await jobActions.downloadSpool(undefined as any); expect(mocked(Gui.errorMessage).mock.calls.length).toBe(1); }); }); describe("Jobs Actions Unit Tests - Function downloadSingleSpool", () => { - function createBlockMocks() { - const session = createISession(); - const iJob = createIJobObject(); - const imperativeProfile = createIProfile(); - const jesApi = createJesApi(imperativeProfile); - bindJesApi(jesApi); - - return { - session, - iJob, - imperativeProfile, - jesApi, - }; - } - it("Checking download of Job Spool", async () => { - createGlobalMocks(); - const blockMocks = createBlockMocks(); + const blockMocks = createGlobalMocks(); const iJobFile = createIJobFile(); const jobs: Job[] = []; const node = new Job( "test:dd - 1", vscode.TreeItemCollapsibleState.None, - null, + null as any, blockMocks.session, blockMocks.iJob, blockMocks.imperativeProfile @@ -407,14 +359,13 @@ describe("Jobs Actions Unit Tests - Function downloadSingleSpool", () => { }); it("should fail to download single spool files if the extender has not implemented the operation", async () => { - createGlobalMocks(); - const blockMocks = createBlockMocks(); + const blockMocks = createGlobalMocks(); const iJobFile = createIJobFile(); const jobs: Job[] = []; const node = new Job( "test:dd - 1", vscode.TreeItemCollapsibleState.None, - null, + null as any, blockMocks.session, blockMocks.iJob, blockMocks.imperativeProfile @@ -442,22 +393,16 @@ describe("Jobs Actions Unit Tests - Function downloadSingleSpool", () => { }); describe("Jobs Actions Unit Tests - Function downloadJcl", () => { - function createBlockMocks() { - const session = createISession(); - const iJob = createIJobObject(); - const imperativeProfile = createIProfile(); - - return { - session, - iJob, - imperativeProfile, - }; - } - it("Checking download of Job JCL", async () => { - createGlobalMocks(); - const blockMocks = createBlockMocks(); - const node = new Job("job", vscode.TreeItemCollapsibleState.None, null, blockMocks.session, blockMocks.iJob, blockMocks.imperativeProfile); + const blockMocks = createGlobalMocks(); + const node = new Job( + "job", + vscode.TreeItemCollapsibleState.None, + null as any, + blockMocks.session, + blockMocks.iJob, + blockMocks.imperativeProfile + ); await jobActions.downloadJcl(node); expect(mocked(zowe.GetJobs.getJclForJob)).toBeCalled(); expect(mocked(vscode.workspace.openTextDocument)).toBeCalled(); @@ -465,8 +410,7 @@ describe("Jobs Actions Unit Tests - Function downloadJcl", () => { }); it("Checking failed attempt to download Job JCL", async () => { createGlobalMocks(); - const blockMocks = createBlockMocks(); - await jobActions.downloadJcl(undefined); + await jobActions.downloadJcl(undefined as any); expect(mocked(Gui.errorMessage)).toBeCalled(); }); }); @@ -479,14 +423,17 @@ describe("Jobs Actions Unit Tests - Function submitJcl", () => { const imperativeProfile = createIProfile(); const datasetSessionNode = createDatasetSessionNode(session, imperativeProfile); const textDocument = createTextDocument("HLQ.TEST.AFILE(mem)", datasetSessionNode); + (textDocument.languageId as any) = "jcl"; const profileInstance = createInstanceOfProfile(imperativeProfile); const jesApi = createJesApi(imperativeProfile); const mockCheckCurrentProfile = jest.fn(); bindJesApi(jesApi); - Object.defineProperty(profileInstance, "loadNamedProfile", { value: jest.fn(), + configurable: true, }); + const errorGuiMsgSpy = jest.spyOn(Gui, "errorMessage"); + const errorLogSpy = jest.spyOn(ZoweLogger, "error"); return { session, @@ -499,6 +446,8 @@ describe("Jobs Actions Unit Tests - Function submitJcl", () => { profileInstance, jesApi, mockCheckCurrentProfile, + errorLogSpy, + errorGuiMsgSpy, }; } @@ -512,15 +461,44 @@ describe("Jobs Actions Unit Tests - Function submitJcl", () => { resolve(blockMocks.datasetSessionNode.label); }) ); + const mockFile = { + path: "/fake/path/file.txt", + } as vscode.Uri; blockMocks.testDatasetTree.getChildren.mockResolvedValueOnce([ - new ZoweDatasetNode("node", vscode.TreeItemCollapsibleState.None, blockMocks.datasetSessionNode, null), + new ZoweDatasetNode("node", vscode.TreeItemCollapsibleState.None, blockMocks.datasetSessionNode, null as any), blockMocks.datasetSessionNode, ]); + const commandSpy = jest.spyOn(vscode.commands, "executeCommand"); activeTextEditorDocument.mockReturnValue(blockMocks.textDocument); const submitJclSpy = jest.spyOn(blockMocks.jesApi, "submitJcl"); submitJclSpy.mockClear(); submitJclSpy.mockResolvedValueOnce(blockMocks.iJob); - await dsActions.submitJcl(blockMocks.testDatasetTree); + await dsActions.submitJcl(blockMocks.testDatasetTree, mockFile); + + expect(commandSpy).toBeCalled(); + expect(submitJclSpy).toBeCalled(); + expect(mocked(Gui.showMessage)).toBeCalled(); + expect(mocked(Gui.showMessage).mock.calls.length).toBe(1); + expect(mocked(Gui.showMessage).mock.calls[0][0]).toEqual( + "Job submitted [JOB1234](command:zowe.jobs.setJobSpool?%5B%22sestest%22%2C%22JOB1234%22%5D)" + ); + commandSpy.mockClear(); + }); + it("Checking submit of JCL file from VSC explorer tree", async () => { + createGlobalMocks(); + const blockMocks: any = createBlockMocks(); + mocked(zowe.ZosmfSession.createSessCfgFromArgs).mockReturnValue(blockMocks.session); + mocked(Profiles.getInstance).mockReturnValue(blockMocks.profileInstance); + mocked(vscode.window.showQuickPick).mockReturnValueOnce(Promise.resolve(blockMocks.datasetSessionNode.label)); + blockMocks.testDatasetTree.getChildren.mockResolvedValueOnce([ + new ZoweDatasetNode("node", vscode.TreeItemCollapsibleState.None, blockMocks.datasetSessionNode, null as any), + blockMocks.datasetSessionNode, + ]); + activeTextEditorDocument.mockReturnValue(blockMocks.textDocument); + const submitJclSpy = jest.spyOn(blockMocks.jesApi, "submitJcl"); + submitJclSpy.mockClear(); + submitJclSpy.mockResolvedValueOnce(blockMocks.iJob); + await dsActions.submitJcl(blockMocks.testDatasetTree, undefined); expect(submitJclSpy).toBeCalled(); expect(mocked(Gui.showMessage)).toBeCalled(); @@ -541,14 +519,14 @@ describe("Jobs Actions Unit Tests - Function submitJcl", () => { }) ); blockMocks.testDatasetTree.getChildren.mockResolvedValueOnce([ - new ZoweDatasetNode("node", vscode.TreeItemCollapsibleState.None, blockMocks.datasetSessionNode, null), + new ZoweDatasetNode("node", vscode.TreeItemCollapsibleState.None, blockMocks.datasetSessionNode, null as any), blockMocks.datasetSessionNode, ]); activeTextEditorDocument.mockReturnValue(blockMocks.textDocument); const submitJclSpy = jest.spyOn(blockMocks.jesApi, "submitJcl"); submitJclSpy.mockClear(); submitJclSpy.mockResolvedValueOnce(blockMocks.iJob); - await dsActions.submitJcl(blockMocks.testDatasetTree); + await dsActions.submitJcl(blockMocks.testDatasetTree, undefined); expect(submitJclSpy).toBeCalled(); expect(mocked(Gui.showMessage)).toBeCalled(); @@ -557,6 +535,20 @@ describe("Jobs Actions Unit Tests - Function submitJcl", () => { "Job submitted [JOB1234](command:zowe.jobs.setJobSpool?%5B%22sestest%22%2C%22JOB1234%22%5D)" ); }); + it("Checking failure of submitting JCL via command palette if not active text editor", async () => { + createGlobalMocks(); + const blockMocks = createBlockMocks(); + Object.defineProperty(vscode.window, "activeTextEditor", { + value: undefined, + configurable: true, + }); + + await dsActions.submitJcl(blockMocks.testDatasetTree, undefined); + + const errorMsg = "No editor with a document that could be submitted as JCL is currently open."; + expect(blockMocks.errorLogSpy).toBeCalledWith(errorMsg); + expect(blockMocks.errorGuiMsgSpy).toBeCalledWith(errorMsg); + }); it("Checking failed attempt to submit of active text editor content as JCL without profile chosen from quickpick", async () => { createGlobalMocks(); @@ -565,7 +557,7 @@ describe("Jobs Actions Unit Tests - Function submitJcl", () => { mocked(Profiles.getInstance).mockReturnValue(blockMocks.profileInstance); mocked(vscode.window.showQuickPick).mockResolvedValueOnce(undefined); // Here we imitate the case when no profile was selected blockMocks.testDatasetTree.getChildren.mockResolvedValueOnce([ - new ZoweDatasetNode("node", vscode.TreeItemCollapsibleState.None, blockMocks.datasetSessionNode, null), + new ZoweDatasetNode("node", vscode.TreeItemCollapsibleState.None, blockMocks.datasetSessionNode, null as any), blockMocks.datasetSessionNode, ]); activeTextEditorDocument.mockReturnValue(blockMocks.textDocument); @@ -573,7 +565,7 @@ describe("Jobs Actions Unit Tests - Function submitJcl", () => { const submitJclSpy = jest.spyOn(blockMocks.jesApi, "submitJcl"); submitJclSpy.mockClear(); - await dsActions.submitJcl(blockMocks.testDatasetTree); + await dsActions.submitJcl(blockMocks.testDatasetTree, undefined); expect(submitJclSpy).not.toBeCalled(); expect(messageSpy).toBeCalledWith("Operation Cancelled"); @@ -590,7 +582,7 @@ describe("Jobs Actions Unit Tests - Function submitJcl", () => { }) ); blockMocks.testDatasetTree.getChildren.mockResolvedValueOnce([ - new ZoweDatasetNode("node", vscode.TreeItemCollapsibleState.None, blockMocks.datasetSessionNode, null), + new ZoweDatasetNode("node", vscode.TreeItemCollapsibleState.None, blockMocks.datasetSessionNode, null as any), blockMocks.datasetSessionNode, ]); activeTextEditorDocument.mockReturnValue(blockMocks.textDocument); @@ -598,7 +590,7 @@ describe("Jobs Actions Unit Tests - Function submitJcl", () => { submitJclSpy.mockClear(); const testError = new Error("submitJcl failed"); submitJclSpy.mockRejectedValueOnce(testError); - await dsActions.submitJcl(blockMocks.testDatasetTree); + await dsActions.submitJcl(blockMocks.testDatasetTree, undefined); expect(submitJclSpy).toBeCalled(); expect(mocked(Gui.errorMessage)).toBeCalled(); @@ -632,9 +624,9 @@ describe("Jobs Actions Unit Tests - Function submitMember", () => { createGlobalMocks(); const blockMocks = createBlockMocks(); mocked(Profiles.getInstance).mockReturnValue(blockMocks.profileInstance); - const subNode = new ZoweDatasetNode("dataset", vscode.TreeItemCollapsibleState.Collapsed, blockMocks.datasetSessionNode, null); + const subNode = new ZoweDatasetNode("dataset", vscode.TreeItemCollapsibleState.Collapsed, blockMocks.datasetSessionNode, null as any); subNode.contextValue = globals.DS_PDS_CONTEXT; - const member = new ZoweDatasetNode("member", vscode.TreeItemCollapsibleState.None, subNode, null); + const member = new ZoweDatasetNode("member", vscode.TreeItemCollapsibleState.None, subNode, null as any); member.contextValue = globals.DS_MEMBER_CONTEXT; const submitJobSpy = jest.spyOn(blockMocks.jesApi, "submitJob"); submitJobSpy.mockResolvedValueOnce(blockMocks.iJob); @@ -662,9 +654,9 @@ describe("Jobs Actions Unit Tests - Function submitMember", () => { }; }), }); - const subNode = new ZoweDatasetNode("dataset", vscode.TreeItemCollapsibleState.Collapsed, blockMocks.datasetSessionNode, null); + const subNode = new ZoweDatasetNode("dataset", vscode.TreeItemCollapsibleState.Collapsed, blockMocks.datasetSessionNode, null as any); subNode.contextValue = globals.DS_PDS_CONTEXT; - const member = new ZoweDatasetNode("member", vscode.TreeItemCollapsibleState.None, subNode, null); + const member = new ZoweDatasetNode("member", vscode.TreeItemCollapsibleState.None, subNode, null as any); member.contextValue = globals.DS_MEMBER_CONTEXT; const submitJobSpy = jest.spyOn(blockMocks.jesApi, "submitJob"); submitJobSpy.mockResolvedValueOnce(blockMocks.iJob); @@ -681,7 +673,7 @@ describe("Jobs Actions Unit Tests - Function submitMember", () => { createGlobalMocks(); const blockMocks = createBlockMocks(); mocked(Profiles.getInstance).mockReturnValue(blockMocks.profileInstance); - const dataset = new ZoweDatasetNode("dataset", vscode.TreeItemCollapsibleState.Collapsed, blockMocks.datasetSessionNode, null); + const dataset = new ZoweDatasetNode("dataset", vscode.TreeItemCollapsibleState.Collapsed, blockMocks.datasetSessionNode, null as any); dataset.contextValue = globals.DS_DS_CONTEXT; const submitJobSpy = jest.spyOn(blockMocks.jesApi, "submitJob"); submitJobSpy.mockClear(); @@ -699,11 +691,11 @@ describe("Jobs Actions Unit Tests - Function submitMember", () => { createGlobalMocks(); const blockMocks = createBlockMocks(); mocked(Profiles.getInstance).mockReturnValue(blockMocks.profileInstance); - const favProfileNode = new ZoweDatasetNode("test", vscode.TreeItemCollapsibleState.Collapsed, blockMocks.datasetSessionNode, null); + const favProfileNode = new ZoweDatasetNode("test", vscode.TreeItemCollapsibleState.Collapsed, blockMocks.datasetSessionNode, null as any); favProfileNode.contextValue = globals.FAV_PROFILE_CONTEXT; - const favoriteSubNode = new ZoweDatasetNode("TEST.JCL", vscode.TreeItemCollapsibleState.Collapsed, favProfileNode, null); + const favoriteSubNode = new ZoweDatasetNode("TEST.JCL", vscode.TreeItemCollapsibleState.Collapsed, favProfileNode, null as any); favoriteSubNode.contextValue = globals.DS_PDS_CONTEXT + globals.FAV_SUFFIX; - const favoriteMember = new ZoweDatasetNode(globals.DS_PDS_CONTEXT, vscode.TreeItemCollapsibleState.Collapsed, favoriteSubNode, null); + const favoriteMember = new ZoweDatasetNode(globals.DS_PDS_CONTEXT, vscode.TreeItemCollapsibleState.Collapsed, favoriteSubNode, null as any); favoriteMember.contextValue = globals.DS_MEMBER_CONTEXT; const submitJobSpy = jest.spyOn(blockMocks.jesApi, "submitJob"); submitJobSpy.mockClear(); @@ -721,9 +713,9 @@ describe("Jobs Actions Unit Tests - Function submitMember", () => { createGlobalMocks(); const blockMocks = createBlockMocks(); mocked(Profiles.getInstance).mockReturnValue(blockMocks.profileInstance); - const favProfileNode = new ZoweDatasetNode("test", vscode.TreeItemCollapsibleState.Collapsed, blockMocks.datasetSessionNode, null); + const favProfileNode = new ZoweDatasetNode("test", vscode.TreeItemCollapsibleState.Collapsed, blockMocks.datasetSessionNode, null as any); favProfileNode.contextValue = globals.FAV_PROFILE_CONTEXT; - const favoriteDataset = new ZoweDatasetNode("TEST.JCL", vscode.TreeItemCollapsibleState.Collapsed, favProfileNode, null); + const favoriteDataset = new ZoweDatasetNode("TEST.JCL", vscode.TreeItemCollapsibleState.Collapsed, favProfileNode, null as any); favoriteDataset.contextValue = globals.DS_DS_CONTEXT + globals.FAV_SUFFIX; const submitJobSpy = jest.spyOn(blockMocks.jesApi, "submitJob"); submitJobSpy.mockClear(); @@ -741,9 +733,9 @@ describe("Jobs Actions Unit Tests - Function submitMember", () => { createGlobalMocks(); const blockMocks = createBlockMocks(); mocked(Profiles.getInstance).mockReturnValue(blockMocks.profileInstance); - const corruptedNode = new ZoweDatasetNode("gibberish", vscode.TreeItemCollapsibleState.Collapsed, blockMocks.datasetSessionNode, null); + const corruptedNode = new ZoweDatasetNode("gibberish", vscode.TreeItemCollapsibleState.Collapsed, blockMocks.datasetSessionNode, null as any); corruptedNode.contextValue = "gibberish"; - const corruptedSubNode = new ZoweDatasetNode("gibberishmember", vscode.TreeItemCollapsibleState.Collapsed, corruptedNode, null); + const corruptedSubNode = new ZoweDatasetNode("gibberishmember", vscode.TreeItemCollapsibleState.Collapsed, corruptedNode, null as any); const submitJobSpy = jest.spyOn(blockMocks.jesApi, "submitJob"); submitJobSpy.mockClear(); submitJobSpy.mockResolvedValueOnce(blockMocks.iJob); @@ -764,7 +756,12 @@ describe("Jobs Actions Unit Tests - Function submitMember", () => { const blockMocks = createBlockMocks(); mocked(Profiles.getInstance).mockReturnValue(blockMocks.profileInstance); - const dataset = new ZoweDatasetNode("TESTUSER.DATASET", vscode.TreeItemCollapsibleState.Collapsed, blockMocks.datasetSessionNode, null); + const dataset = new ZoweDatasetNode( + "TESTUSER.DATASET", + vscode.TreeItemCollapsibleState.Collapsed, + blockMocks.datasetSessionNode, + null as any + ); dataset.contextValue = globals.DS_DS_CONTEXT; for (let o = 0; o < sharedUtils.JOB_SUBMIT_DIALOG_OPTS.length; o++) { @@ -801,7 +798,7 @@ describe("Jobs Actions Unit Tests - Function submitMember", () => { } // Test for "Cancel" or closing the dialog - mocked(Gui.warningMessage).mockReturnValueOnce(undefined); + mocked(Gui.warningMessage).mockReturnValueOnce(undefined as any); await dsActions.submitMember(dataset); expect(mocked(Gui.warningMessage)).toBeCalledWith("Are you sure you want to submit the following job?\n\n" + dataset.getLabel(), { items: [{ title: "Submit" }], @@ -933,7 +930,7 @@ describe("Jobs Actions Unit Tests - Function getSpoolContent", () => { vscode.TreeItemCollapsibleState.None, createJobFavoritesNode(), createISessionWithoutCredentials(), - null, + null as any, createIProfile() ); jest.spyOn(Spool.prototype, "getProfile").mockReturnValue({ @@ -1086,7 +1083,7 @@ describe("Jobs Actions Unit Tests - Function refreshJobsServer", () => { const job = new Job( "jobtest", vscode.TreeItemCollapsibleState.Expanded, - null, + null as any, blockMocks.session, blockMocks.iJob, blockMocks.imperativeProfile @@ -1103,7 +1100,7 @@ describe("Jobs Actions Unit Tests - Function refreshJobsServer", () => { const job = new Job( "jobtest", vscode.TreeItemCollapsibleState.Expanded, - null, + null as any, blockMocks.session, blockMocks.iJob, blockMocks.imperativeProfile @@ -1239,7 +1236,7 @@ describe("job deletion command", () => { const jobsProvider = createJobsTree(session, job, profile, createTreeView()); jobsProvider.delete.mockResolvedValueOnce(Promise.resolve()); - const jobNode = new Job("jobtest", vscode.TreeItemCollapsibleState.Expanded, null, session, job, profile); + const jobNode = new Job("jobtest", vscode.TreeItemCollapsibleState.Expanded, null as any, session, job, profile); await jobActions.deleteCommand(jobsProvider, jobNode); @@ -1253,8 +1250,8 @@ describe("job deletion command", () => { const jobsProvider = createJobsTree(session, job, profile, createTreeView()); jobsProvider.mSessionNodes.push(job2); jobsProvider.delete.mockResolvedValue(Promise.resolve()); - const jobNode1 = new Job("jobtest1", vscode.TreeItemCollapsibleState.Expanded, null, session, job, profile); - const jobNode2 = new Job("jobtest2", vscode.TreeItemCollapsibleState.Expanded, null, session, job2, profile); + const jobNode1 = new Job("jobtest1", vscode.TreeItemCollapsibleState.Expanded, null as any, session, job, profile); + const jobNode2 = new Job("jobtest2", vscode.TreeItemCollapsibleState.Expanded, null as any, session, job2, profile); const jobs = [jobNode1, jobNode2]; // act await jobActions.deleteCommand(jobsProvider, undefined, jobs); @@ -1268,7 +1265,7 @@ describe("job deletion command", () => { const jobsProvider = createJobsTree(session, job, profile, createTreeView()); jobsProvider.delete.mockResolvedValueOnce(Promise.resolve()); - const jobNode = new Job("jobtest", vscode.TreeItemCollapsibleState.Expanded, null, session, job, profile); + const jobNode = new Job("jobtest", vscode.TreeItemCollapsibleState.Expanded, null as any, session, job, profile); await jobActions.deleteCommand(jobsProvider, jobNode); expect(mocked(jobsProvider.delete)).not.toBeCalled(); @@ -1279,7 +1276,7 @@ describe("job deletion command", () => { const jobsProvider = createJobsTree(session, job, profile, createTreeView()); jobsProvider.delete.mockResolvedValueOnce(Promise.reject(new Error("something went wrong!"))); - const jobNode = new Job("jobtest", vscode.TreeItemCollapsibleState.Expanded, null, session, job, profile); + const jobNode = new Job("jobtest", vscode.TreeItemCollapsibleState.Expanded, null as any, session, job, profile); // act await jobActions.deleteCommand(jobsProvider, jobNode); // assert @@ -1292,7 +1289,7 @@ describe("job deletion command", () => { const jobsProvider = createJobsTree(session, job, profile, createTreeView()); jobsProvider.delete.mockResolvedValueOnce(Promise.resolve()); - const jobNode = new Job("jobtest", vscode.TreeItemCollapsibleState.Expanded, null, session, job, profile); + const jobNode = new Job("jobtest", vscode.TreeItemCollapsibleState.Expanded, null as any, session, job, profile); jobsProvider.getTreeView.mockReturnValueOnce({ ...jobsProvider.getTreeView(), selection: [jobNode] }); // act await jobActions.deleteCommand(jobsProvider, undefined); @@ -1307,7 +1304,7 @@ describe("Job Actions Unit Tests - Misc. functions", () => { const session = createISession(); const profile = createIProfile(); const job = createIJobObject(); - const jobNode = new Job("job", vscode.TreeItemCollapsibleState.None, null, session, job, profile); + const jobNode = new Job("job", vscode.TreeItemCollapsibleState.None, null as any, session, job, profile); it("refreshJob works as intended", () => { const jobsProvider = createJobsTree(session, job, profile, createTreeView()); @@ -1357,8 +1354,8 @@ describe("sortJobs function", () => { method: JobSortOpts.Id, direction: SortDirection.Ascending, }; - testtree.mSessionNodes[0].children = [...[globalMocks()[2], globalMocks()[1], globalMocks()[0]]]; - expected.mSessionNodes[0].children = [...[globalMocks()[1], globalMocks()[0], globalMocks()[2]]]; + testtree.mSessionNodes[0].children = [...[globalMocks.mockJobArray[2], globalMocks.mockJobArray[1], globalMocks.mockJobArray[0]]]; + expected.mSessionNodes[0].children = [...[globalMocks.mockJobArray[1], globalMocks.mockJobArray[0], globalMocks.mockJobArray[2]]]; jest.spyOn(Gui, "showQuickPick").mockResolvedValueOnce({ label: "$(case-sensitive) Job Name" }); const sortbynamespy = jest.spyOn(ZosJobsProvider.prototype, "sortBy"); //act @@ -1376,8 +1373,8 @@ describe("sortJobs function", () => { method: JobSortOpts.Id, direction: SortDirection.Ascending, }; - testtree.mSessionNodes[0].children = [...[globalMocks()[2], globalMocks()[1], globalMocks()[0]]]; - expected.mSessionNodes[0].children = [...[globalMocks()[1], globalMocks()[0], globalMocks()[2]]]; + testtree.mSessionNodes[0].children = [...[globalMocks.mockJobArray[2], globalMocks.mockJobArray[1], globalMocks.mockJobArray[0]]]; + expected.mSessionNodes[0].children = [...[globalMocks.mockJobArray[1], globalMocks.mockJobArray[0], globalMocks.mockJobArray[2]]]; const sortbyidspy = jest.spyOn(ZosJobsProvider.prototype, "sortBy"); jest.spyOn(Gui, "showQuickPick").mockResolvedValueOnce({ label: "$(list-ordered) Job ID (default)" }); //act @@ -1395,8 +1392,8 @@ describe("sortJobs function", () => { method: JobSortOpts.Id, direction: SortDirection.Ascending, }; - testtree.mSessionNodes[0].children = [...[globalMocks()[2], globalMocks()[1], globalMocks()[0]]]; - expected.mSessionNodes[0].children = [...[globalMocks()[0], globalMocks()[1], globalMocks()[2]]]; + testtree.mSessionNodes[0].children = [...[globalMocks.mockJobArray[2], globalMocks.mockJobArray[1], globalMocks.mockJobArray[0]]]; + expected.mSessionNodes[0].children = [...[globalMocks.mockJobArray[0], globalMocks.mockJobArray[1], globalMocks.mockJobArray[2]]]; const sortbyretcodespy = jest.spyOn(ZosJobsProvider.prototype, "sortBy"); jest.spyOn(Gui, "showQuickPick").mockResolvedValueOnce({ label: "$(symbol-numeric) Return Code" }); @@ -1415,7 +1412,7 @@ describe("sortJobs function", () => { method: JobSortOpts.Id, direction: SortDirection.Ascending, }; - testtree.mSessionNodes[0].children = [globalMocks()[0]]; + testtree.mSessionNodes[0].children = [globalMocks.mockJobArray[0]]; const jobsSortBy = jest.spyOn(ZosJobsProvider.prototype, "sortBy"); const quickPickSpy = jest.spyOn(Gui, "showQuickPick").mockResolvedValueOnce({ label: "$(fold) Sort Direction" }); quickPickSpy.mockResolvedValueOnce("Descending" as any); diff --git a/packages/zowe-explorer/i18n/sample/package.i18n.json b/packages/zowe-explorer/i18n/sample/package.i18n.json index a5a3599fe6..e3c8819b8b 100644 --- a/packages/zowe-explorer/i18n/sample/package.i18n.json +++ b/packages/zowe-explorer/i18n/sample/package.i18n.json @@ -40,7 +40,7 @@ "removeSavedSearch": "Remove Search", "removeSession": "Hide Profile", "saveSearch": "Add to Favorites", - "submitJcl": "Submit JCL", + "submitJcl": "Submit as JCL", "submitMember": "Submit Job", "uss.addSession": "Add Profile to USS View", "uss.copyPath": "Copy Path", diff --git a/packages/zowe-explorer/i18n/sample/src/dataset/actions.i18n.json b/packages/zowe-explorer/i18n/sample/src/dataset/actions.i18n.json index aa1718439c..209867b8c6 100644 --- a/packages/zowe-explorer/i18n/sample/src/dataset/actions.i18n.json +++ b/packages/zowe-explorer/i18n/sample/src/dataset/actions.i18n.json @@ -42,8 +42,8 @@ "showAttributes.lengthError": "No matching names found for query: {0}", "showAttributes.error": "Unable to list attributes.", "attributes.title": "Attributes", - "submitJcl.noDocumentOpen": "No editor with a document that could be submitted as JCL is currently open.", - "submitJcl.submitting": "Submitting JCL in document {0}", + "submitJcl.notActiveEditorMsg": "No editor with a document that could be submitted as JCL is currently open.", + "submitJcl.submitting": "Submitting as JCL in document {0}", "submitJcl.qp.placeholder": "Select the Profile to use to submit the job", "submitJcl.noProfile": "No profiles available", "submitJcl.nullSession.error": "Session for submitting JCL was null or undefined!", diff --git a/packages/zowe-explorer/package.json b/packages/zowe-explorer/package.json index a7be51ddb8..001a2b6772 100644 --- a/packages/zowe-explorer/package.json +++ b/packages/zowe-explorer/package.json @@ -807,6 +807,20 @@ } ], "menus": { + "editor/context": [ + { + "when": "editorFocus", + "command": "zowe.ds.submitJcl", + "group": "000_zowe_dsMainframeInteraction@1" + } + ], + "explorer/context": [ + { + "when": "!explorerResourceIsFolder", + "command": "zowe.ds.submitJcl", + "group": "000_zowe_dsMainframeInteraction@1" + } + ], "view/title": [ { "when": "view == zowe.ds.explorer", diff --git a/packages/zowe-explorer/package.nls.json b/packages/zowe-explorer/package.nls.json index a5a3599fe6..e3c8819b8b 100644 --- a/packages/zowe-explorer/package.nls.json +++ b/packages/zowe-explorer/package.nls.json @@ -40,7 +40,7 @@ "removeSavedSearch": "Remove Search", "removeSession": "Hide Profile", "saveSearch": "Add to Favorites", - "submitJcl": "Submit JCL", + "submitJcl": "Submit as JCL", "submitMember": "Submit Job", "uss.addSession": "Add Profile to USS View", "uss.copyPath": "Copy Path", diff --git a/packages/zowe-explorer/src/dataset/actions.ts b/packages/zowe-explorer/src/dataset/actions.ts index 7c7d64c02e..b7e6c9e6c0 100644 --- a/packages/zowe-explorer/src/dataset/actions.ts +++ b/packages/zowe-explorer/src/dataset/actions.ts @@ -967,22 +967,28 @@ export async function showAttributes(node: api.IZoweDatasetTreeNode, datasetProv } /** - * Submit the contents of the editor as JCL. + * Submit the contents of the editor or file as JCL. * * @export * @param {DatasetTree} datasetProvider - our DatasetTree object */ // This function does not appear to currently be made available in the UI -export async function submitJcl(datasetProvider: api.IZoweTree): Promise { +export async function submitJcl(datasetProvider: api.IZoweTree, file?: vscode.Uri): Promise { ZoweLogger.trace("dataset.actions.submitJcl called."); - if (!vscode.window.activeTextEditor) { - const errorMsg = localize("submitJcl.noDocumentOpen", "No editor with a document that could be submitted as JCL is currently open."); - api.Gui.errorMessage(errorMsg); - ZoweLogger.error(errorMsg); + if (!vscode.window.activeTextEditor && !file) { + const notActiveEditorMsg = localize( + "submitJcl.notActiveEditorMsg", + "No editor with a document that could be submitted as JCL is currently open." + ); + api.Gui.errorMessage(notActiveEditorMsg); + ZoweLogger.error(notActiveEditorMsg); return; } + if (file) { + await vscode.commands.executeCommand("filesExplorer.openFilePreserveFocus", file); + } const doc = vscode.window.activeTextEditor.document; - ZoweLogger.debug(localize("submitJcl.submitting", "Submitting JCL in document {0}", doc.fileName)); + ZoweLogger.debug(localize("submitJcl.submitting", "Submitting as JCL in document {0}", doc.fileName)); // get session name const sessionregex = /\[(.*)(\])(?!.*\])/g; const regExp = sessionregex.exec(doc.fileName); diff --git a/packages/zowe-explorer/src/dataset/init.ts b/packages/zowe-explorer/src/dataset/init.ts index 01ffc33e0f..10c508139d 100644 --- a/packages/zowe-explorer/src/dataset/init.ts +++ b/packages/zowe-explorer/src/dataset/init.ts @@ -131,7 +131,7 @@ export async function initDatasetProvider(context: vscode.ExtensionContext): Pro context.subscriptions.push( vscode.commands.registerCommand("zowe.ds.removeFavProfile", (node) => datasetProvider.removeFavProfile(node.label, true)) ); - context.subscriptions.push(vscode.commands.registerCommand("zowe.ds.submitJcl", async () => dsActions.submitJcl(datasetProvider))); + context.subscriptions.push(vscode.commands.registerCommand("zowe.ds.submitJcl", async (file) => dsActions.submitJcl(datasetProvider, file))); context.subscriptions.push(vscode.commands.registerCommand("zowe.ds.submitMember", async (node) => dsActions.submitMember(node))); context.subscriptions.push( vscode.commands.registerCommand("zowe.ds.showAttributes", async (node, nodeList) => {