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

Issue #968b: Group jobs’ favorites by profile and allow extenders to activate before loading profiles #991

Merged
merged 29 commits into from
Sep 11, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
ae6ce81
Begin grouping job favorites by profile
lauren-li Aug 23, 2020
a615f80
Add loaded profile and session to jobs favorite profile node
lauren-li Aug 23, 2020
f446e7e
Update Jobs functions for adding a new favorite
lauren-li Aug 24, 2020
8b32597
Update Jobs removeFavorite for new favorites hierarchy
lauren-li Aug 24, 2020
d9b1341
Update Jobs addFavorite for saving searches
lauren-li Aug 24, 2020
426fcc7
Fix tooltips in Job favorites
lauren-li Aug 24, 2020
a86c69e
Update Job Node getChildren for new favorites labeling
lauren-li Aug 24, 2020
78f7de5
Fix syncing of Jobs deleteSession with favorites
lauren-li Aug 25, 2020
8c92cc9
Fix unit tests for Jobs addFavorite and removeFavorite
lauren-li Aug 25, 2020
d16e23b
Add unit test for adding favorite search
lauren-li Aug 25, 2020
e505028
Correct mParent when adding job/search favorites
lauren-li Aug 25, 2020
fd32f3e
Update Jobs saveSearch and related unit tests
lauren-li Aug 25, 2020
441631f
Add unit tests for Jobs Tree getChildren
lauren-li Aug 26, 2020
dfb87f3
Add unit tests for Jobs initializeFavChildNodeForProfile
lauren-li Aug 26, 2020
df0a033
Correct parent node for saved Jobs search in initializeFavChildNodeF…
lauren-li Aug 26, 2020
b4704a8
Add unit tests for Jobs loadProfilesForFavorites
lauren-li Aug 26, 2020
55a0763
Refactor Jobs updateFavorites to reuse function from PersistentFilters
lauren-li Aug 27, 2020
f2a2bbc
Add context value checking to Jobs addFavorite unit tests
lauren-li Aug 27, 2020
73851c0
Add error handling case for delete Job unit test
lauren-li Aug 27, 2020
36a0847
Add unit test for Jobs searchPrompt with favorited search
lauren-li Aug 27, 2020
158732e
Update i18n
lauren-li Aug 27, 2020
ff30871
Minor JSDoc edit
lauren-li Aug 27, 2020
c0aa402
Fix deleting profile node in Favorites when profile is deleted
lauren-li Aug 28, 2020
9cf141a
Localize deleteProfile success message
lauren-li Aug 28, 2020
bafed21
Merge branch 'load-favs-by-profile-jobs' of github.com:zowe/vscode-ex…
lauren-li Aug 28, 2020
57e9fe9
Sort profile nodes in Jobs favorites
lauren-li Sep 10, 2020
f2f8b26
Merge in master and resolve conflicts
lauren-li Sep 10, 2020
663c298
Sort profile nodes in DS favorites
lauren-li Sep 10, 2020
513593c
Merge branch 'master' into load-favs-by-profile-jobs
zFernand0 Sep 11, 2020
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
38 changes: 38 additions & 0 deletions __mocks__/mockCreators/jobs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,3 +95,41 @@ export function createJobsTree(session: imperative.Session, iJob: zowe.IJob, pro

return testJobsTree;
}

export function createJobSessionNode(session: imperative.Session, profile: imperative.IProfileLoaded) {
const jobSessionNode = new Job("sestest", vscode.TreeItemCollapsibleState.Collapsed,
null, session, null, profile);
jobSessionNode.contextValue = globals.JOBS_SESSION_CONTEXT;

return jobSessionNode;
}

export function createJobFavoritesNode() {
const jobFavoritesNode = new Job("Favorites", vscode.TreeItemCollapsibleState.Collapsed, null, null, null, null);
jobFavoritesNode.contextValue = globals.FAVORITE_CONTEXT;

return jobFavoritesNode;
}

// Because the JobDetail class in ZosJobsProvider.ts is not exported:
export class MockJobDetail implements zowe.IJob {
public jobid: string;
public jobname: string;
public subsystem: string;
public owner: string;
public status: string;
public type: string;
public class: string;
public retcode: string;
public url: string;
public "files-url": string;
public "job-correlator": string;
public phase: number;
public "phase-name": string;
public "reason-not-running"?: string;

constructor(combined: string) {
this.jobname = combined.substring(0, combined.indexOf("("));
this.jobid = combined.substring(combined.indexOf("(") + 1, combined.indexOf(")"));
}
}
17 changes: 12 additions & 5 deletions __tests__/__unit__/Profiles.unit.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1172,16 +1172,23 @@ describe("Profiles Unit Tests - Function deleteProfile", () => {

const startLength = blockMocks.testJobTree.mSessionNodes.length;
const favoriteLength = blockMocks.testJobTree.mFavorites.length;
const jobNode = new Job(
"testNode", vscode.TreeItemCollapsibleState.Expanded, null, blockMocks.session, blockMocks.iJob, blockMocks.imperativeProfile);
const jobNodeAsFavorite = new Job(`[${blockMocks.datasetSessionNode.label.trim()}]: testNode`, vscode.TreeItemCollapsibleState.Expanded,
null, blockMocks.session, blockMocks.iJob, blockMocks.imperativeProfile);
// Set up job node
const jobNode = new Job("sestest", vscode.TreeItemCollapsibleState.Expanded, null,
blockMocks.session, blockMocks.iJob, blockMocks.imperativeProfile);
jobNode.contextValue = globals.JOBS_SESSION_CONTEXT;
blockMocks.testJobTree.mSessionNodes.push(jobNode);
blockMocks.testJobTree.addFavorite(jobNodeAsFavorite);
// Set up jobNode in Favorites
const favedJobNode = jobNode;
favedJobNode.contextValue = jobNode.contextValue + globals.FAV_SUFFIX;
const jobProfileNodeInFavs = new Job(`sestest`, vscode.TreeItemCollapsibleState.Expanded, blockMocks.testJobTree.mFavoriteSession,
blockMocks.session, null, blockMocks.imperativeProfile);
jobProfileNodeInFavs.contextValue = globals.FAV_PROFILE_CONTEXT;
jobProfileNodeInFavs.children.push(favedJobNode);
blockMocks.testJobTree.mFavorites.push(jobProfileNodeInFavs);
globalMocks.mockShowQuickPick.mockResolvedValueOnce("Delete");

await blockMocks.profiles.deleteProfile(blockMocks.testDatasetTree, blockMocks.testUSSTree, blockMocks.testJobTree, jobNode);

expect(globalMocks.mockShowInformationMessage.mock.calls.length).toBe(1);
expect(globalMocks.mockShowInformationMessage.mock.calls[0][0]).toBe("Profile sestest was deleted.");
expect(blockMocks.testJobTree.mSessionNodes.length).toEqual(startLength);
Expand Down
393 changes: 393 additions & 0 deletions __tests__/__unit__/job/ZosJobsProvider.unit.test.ts

Large diffs are not rendered by default.

122 changes: 71 additions & 51 deletions __tests__/__unit__/job/ZoweJobNode.unit.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import { Job } from "../../../src/job/ZoweJobNode";
import { Profiles, ValidProfileEnum } from "../../../src/Profiles";
import { createIProfile, createISession, createInstanceOfProfile, createISessionWithoutCredentials, createQuickPickContent } from "../../../__mocks__/mockCreators/shared";
import { ZoweExplorerApiRegister } from "../../../src/api/ZoweExplorerApiRegister";
import { IZoweJobTreeNode } from "../../../src/api/IZoweTreeNode";

async function createGlobalMocks() {
const globalMocks = {
Expand Down Expand Up @@ -196,6 +197,16 @@ describe("ZoweJobNode unit tests - Function delete", () => {
`Job ${globalMocks.testJobNode.job.jobname}(${globalMocks.testJobNode.job.jobid}) deleted`
);
});
it("Tests that delete handles an error thrown during job deletion", async () => {
const globalMocks = await createGlobalMocks();
const badJobNode = new Job("badJobNode",vscode.TreeItemCollapsibleState.Collapsed, null,
globalMocks.testSession, null, globalMocks.testProfile);
const errorHandlingSpy = jest.spyOn(utils, "errorHandling");

await globalMocks.testJobsProvider.delete(badJobNode);

expect(errorHandlingSpy).toBeCalledTimes(1);
});
});

describe("ZoweJobNode unit tests - Function onDidConfiguration", () => {
Expand Down Expand Up @@ -315,13 +326,34 @@ describe("ZoweJobNode unit tests - Function addFavorite", () => {
it("Tests that addFavorite successfully favorites a job", async () => {
const globalMocks = await createGlobalMocks();
const blockMocks = await createBlockMocks(globalMocks);

globalMocks.testJobsProvider.mFavorites = [];
blockMocks.testJobNode.contextValue = globals.JOBS_JOB_CONTEXT;

await globalMocks.testJobsProvider.addFavorite(blockMocks.testJobNode);

const profileNodeInFavs: IZoweJobTreeNode = globalMocks.testJobsProvider.mFavorites[0];
const favoritedNode = profileNodeInFavs.children[0];

expect(globalMocks.testJobsProvider.mFavorites.length).toEqual(1);
expect(globalMocks.testJobsProvider.mFavorites[0].label).toEqual("[sestest]: MYHLQ(JOB1283)");
expect(profileNodeInFavs.label).toEqual("sestest");
expect(profileNodeInFavs.children.length).toEqual(1);
expect(favoritedNode.label).toEqual("MYHLQ(JOB1283)");
expect(favoritedNode.contextValue).toEqual(globals.JOBS_JOB_CONTEXT + globals.FAV_SUFFIX);
});
it("Tests that addFavorite successfully favorites a search", async () => {
const globalMocks = await createGlobalMocks();
await createBlockMocks(globalMocks);
globalMocks.testJobsProvider.mFavorites = [];
globalMocks.testJobsProvider.mSessionNodes[1].owner = "myHLQ";
globalMocks.testJobsProvider.mSessionNodes[1].prefix = "*";
globalMocks.testJobsProvider.mSessionNodes[1].contextValue = globals.JOBS_SESSION_CONTEXT;

await globalMocks.testJobsProvider.addFavorite(globalMocks.testJobsProvider.mSessionNodes[1]);
const profileNodeInFavs: IZoweJobTreeNode = globalMocks.testJobsProvider.mFavorites[0];

expect(profileNodeInFavs.children.length).toEqual(1);
expect(profileNodeInFavs.children[0].label).toEqual("Owner:myHLQ Prefix:*");
expect(profileNodeInFavs.children[0].contextValue).toEqual(globals.JOBS_SESSION_CONTEXT + globals.FAV_SUFFIX);
});
});

Expand All @@ -342,75 +374,46 @@ describe("ZoweJobNode unit tests - Function removeFavorite", () => {
globalMocks.testJobsProvider.mFavorites = [];

await globalMocks.testJobsProvider.addFavorite(blockMocks.testJobNode);
const profileNodeInFavs: IZoweJobTreeNode = globalMocks.testJobsProvider.mFavorites[0];
const favoritedNode = profileNodeInFavs.children[0];

expect(globalMocks.testJobsProvider.mFavorites.length).toEqual(1);
expect(profileNodeInFavs.children.length).toEqual(1);

await globalMocks.testJobsProvider.removeFavorite(globalMocks.testJobsProvider.mFavorites[0]);
await globalMocks.testJobsProvider.removeFavorite(favoritedNode);

expect(globalMocks.testJobsProvider.mFavorites.length).toEqual(0);
expect(profileNodeInFavs.children.length).toEqual(0);
});
});

describe("ZoweJobNode unit tests - Function saveSearch", () => {
async function createBlockMocks(globalMocks) {
const testSession = globalMocks.testJobsProvider.mSessionNodes[1].getSession();
const newMocks = {
testSession,
testJobNode: new Job("MYHLQ(JOB1283) - Input", vscode.TreeItemCollapsibleState.Collapsed, globalMocks.testJobsProvider.mSessionNodes[1],
globalMocks.testJobsProvider.mSessionNodes[1].getSession(), globalMocks.testIJob, globalMocks.testProfile)
testSession, globalMocks.testIJob, globalMocks.testProfile)
};

globalMocks.testJobsProvider.mFavorites = [];
globalMocks.testJobNode.label = "MYHLQ(JOB1283) - Input";
globalMocks.getConfiguration.mockReturnValue({
get: (setting: string) => [
"[sestest]: Owner:stonecc Prefix:*{server}",
"[sestest]: USER1(JOB30148){job}",
],
update: jest.fn(()=>{
return {};
})
});

return newMocks;
}

it("Tests that saveSearch is executed successfully when owner is set", async () => {
const globalMocks = await createGlobalMocks();
await createBlockMocks(globalMocks);

globalMocks.testJobsProvider.mSessionNodes[1].owner = "myHLQ";
globalMocks.testJobsProvider.mSessionNodes[1].prefix = "*";

await globalMocks.testJobsProvider.saveSearch(globalMocks.testJobsProvider.mSessionNodes[1]);

expect(globalMocks.testJobsProvider.mFavorites.length).toEqual(1);
expect(globalMocks.testJobsProvider.mFavorites[0].label).toEqual("[sestest]: Owner:myHLQ Prefix:*");
});

it("Tests that saveSearch is executed successfully when prefix is set", async () => {
it("Tests that saveSearch is executed successfully", async () => {
const globalMocks = await createGlobalMocks();
await createBlockMocks(globalMocks);

globalMocks.testJobsProvider.mSessionNodes[1].owner = "*";
globalMocks.testJobsProvider.mSessionNodes[1].prefix = "aH*";

await globalMocks.testJobsProvider.saveSearch(globalMocks.testJobsProvider.mSessionNodes[1]);

expect(globalMocks.testJobsProvider.mFavorites.length).toEqual(1);
expect(globalMocks.testJobsProvider.mFavorites[0].label).toEqual("[sestest]: Owner:* Prefix:aH*");
});

it("Tests that saveSearch is executed successfully when searchId is set", async () => {
const globalMocks = await createGlobalMocks();
await createBlockMocks(globalMocks);
const blockMocks = await createBlockMocks(globalMocks);
const favJob = blockMocks.testJobNode;
favJob.owner = "myHLQ";
favJob.prefix = "*";
favJob.contextValue = globals.JOBS_SESSION_CONTEXT;

globalMocks.testJobsProvider.mSessionNodes[1].owner = "*";
globalMocks.testJobsProvider.mSessionNodes[1].prefix = "*";
globalMocks.testJobsProvider.mSessionNodes[1].searchId = "JOB1234";
const expectedJob = favJob;
expectedJob.contextValue = globals.JOBS_SESSION_CONTEXT + globals.FAV_SUFFIX;

await globalMocks.testJobsProvider.saveSearch(globalMocks.testJobsProvider.mSessionNodes[1]);
const savedFavJob = await globalMocks.testJobsProvider.saveSearch(favJob);

expect(globalMocks.testJobsProvider.mFavorites.length).toEqual(1);
expect(globalMocks.testJobsProvider.mFavorites[0].label).toEqual("[sestest]: JobId:JOB1234");
expect(savedFavJob).toEqual(expectedJob);
});
});

Expand Down Expand Up @@ -607,8 +610,8 @@ describe("ZoweJobNode unit tests - Function searchPrompt", () => {
const globalMocks = await createGlobalMocks();
await createBlockMocks(globalMocks);

globalMocks.testJobNode.label = "[sestest]: Owner:stonecc Prefix:*";
globalMocks.testJobNode.contextValue = globals.DS_SESSION_CONTEXT + globals.FAV_SUFFIX;
globalMocks.testJobNode.label = "Owner:stonecc Prefix:*";
globalMocks.testJobNode.contextValue = globals.JOBS_SESSION_CONTEXT + globals.FAV_SUFFIX;
const checkSession = jest.spyOn(globalMocks.testJobsProvider, "addSession");
expect(checkSession).not.toHaveBeenCalled();

Expand All @@ -618,6 +621,23 @@ describe("ZoweJobNode unit tests - Function searchPrompt", () => {
expect(checkSession).toHaveBeenLastCalledWith("sestest");
});

it("Testing that searchPrompt from favorited search can pass session values into node in Sessions", async () => {
const globalMocks = await createGlobalMocks();
await createBlockMocks(globalMocks);
globalMocks.testJobNode.label = "Owner:stonecc Prefix:*";
globalMocks.testJobNode.contextValue = globals.JOBS_SESSION_CONTEXT + globals.FAV_SUFFIX;

const sessionNoCreds = createISessionWithoutCredentials();
globalMocks.testJobsProvider.mSessionNodes[1].session = sessionNoCreds;
const sessNodeNoCreds = globalMocks.testJobsProvider.mSessionNodes[1];

await globalMocks.testJobsProvider.searchPrompt(globalMocks.testJobNode);

expect(sessNodeNoCreds.session.ISession.user).toEqual(globalMocks.testJobNode.session.ISession.user);
expect(sessNodeNoCreds.session.ISession.password).toEqual(globalMocks.testJobNode.session.ISession.password);
expect(sessNodeNoCreds.session.ISession.base64EncodedAuth).toEqual(globalMocks.testJobNode.session.ISession.base64EncodedAuth);
});

it("Testing that searchPrompt is successfully executed when searching by owner, Theia route", async () => {
const globalMocks = await createGlobalMocks();
const blockMocks = await createBlockMocks(globalMocks);
Expand Down
1 change: 1 addition & 0 deletions i18n/sample/src/Profiles.i18n.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
"deleteProfile.showQuickPick.cancel": "Cancel",
"deleteProfile.showQuickPick.log.debug": "User picked Cancel. Cancelling delete of profile",
"deleteProfile.delete.log.error": "Error encountered when deleting profile! ",
"deleteProfile.success.info": "Profile {0} was deleted.",
"createNewConnection.option.prompt.url.placeholder": "https://url:port",
"createNewConnection.option.prompt.url": "Enter a z/OS URL in the format 'https://url:port'.",
"createNewConnection.option.prompt.port.placeholder": "Port Number",
Expand Down
4 changes: 3 additions & 1 deletion i18n/sample/src/job/ZosJobsProvider.i18n.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
"Favorites": "Favorites",
"deleteJob.job": "Job ",
"deleteJob.delete": " deleted",
"initializeFavorites.log.debug": "initializing favorites",
"initializeJobsTree.log.debug": "Initializing profiles with jobs favorites.",
"initializeJobsTree.no.favorites": "No jobs favorites found.",
"loadProfilesForFavorites.log.debug": "Loading profile: {0} for jobs favorites",
"initializeJobsFavorites.error.profile1": "Error: You have Jobs favorites that refer to a non-existent CLI profile named: ",
"initializeJobsFavorites.error.profile2": ". To resolve this, you can create a profile with this name, ",
"initializeJobsFavorites.error.profile3": "or remove the favorites with this profile name from the Zowe-Jobs-Persistent setting, which can be found in your ",
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -928,7 +928,7 @@
},
{
"when": "view == zowe.jobs && viewItem =~ /^(?!.*_fav.*)server.*/",
"command": "zowe.jobs.saveSearch",
"command": "zowe.jobs.addFavorite",
"group": "2_workspace@0"
},
{
Expand Down
6 changes: 3 additions & 3 deletions src/Profiles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -914,9 +914,9 @@ export class Profiles {

// Delete from Jobs Favorites
jobsProvider.mFavorites.forEach((ses) => {
const findNode = ses.label.substring(1, ses.label.indexOf("]")).trim();
const findNode = ses.label.trim();
if (findNode === deleteLabel) {
jobsProvider.removeFavorite(ses);
jobsProvider.mFavorites = jobsProvider.mFavorites.filter((tempNode) => tempNode.label.trim() !== findNode);
ses.dirty = true;
jobsProvider.refresh();
}
Expand Down Expand Up @@ -1115,7 +1115,7 @@ export class Profiles {
throw error;
}

vscode.window.showInformationMessage("Profile " + profileName + " was deleted.");
vscode.window.showInformationMessage(localize("deleteProfile.success.info", "Profile {0} was deleted.", profileName));
return profileName;
}

Expand Down
5 changes: 1 addition & 4 deletions src/abstract/ZoweTreeNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,10 +84,7 @@ export class ZoweTreeNode extends vscode.TreeItem {
* @returns {string}
*/
public getProfileName(): string {
if (this.profile) {
return this.profile.name;
}
return undefined;
return this.getProfile() ? this.getProfile().name : undefined;
}

/**
Expand Down
3 changes: 0 additions & 3 deletions src/abstract/ZoweTreeProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -221,9 +221,6 @@ export class ZoweTreeProvider {
}

protected deleteSessionByLabel(revisedLabel: string) {
if (revisedLabel.includes("[")) {
revisedLabel = revisedLabel.substring(0, revisedLabel.indexOf(" ["));
}
this.mHistory.removeSession(revisedLabel);
this.refresh();
}
Expand Down
4 changes: 4 additions & 0 deletions src/api/IZoweTreeNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,10 @@ export interface IZoweUSSTreeNode extends IZoweTreeNode {
* @interface export interface IZoweJobTreeNode extends IZoweTreeNode {
*/
export interface IZoweJobTreeNode extends IZoweTreeNode {
/**
* Use Job-specific tree node for children.
*/
children?: IZoweJobTreeNode[];
/**
* Standard job response document
* Represents the attributes and status of a z/OS batch job
Expand Down
1 change: 1 addition & 0 deletions src/dataset/DatasetTree.ts
Original file line number Diff line number Diff line change
Expand Up @@ -417,6 +417,7 @@ export class DatasetTree extends ZoweTreeProvider implements IZoweTree<IZoweData
)) {
profileNodeInFavorites.children.push(temp);
sortTreeItems(profileNodeInFavorites.children, globals.DS_SESSION_CONTEXT + globals.FAV_SUFFIX);
sortTreeItems(this.mFavorites, globals.FAV_PROFILE_CONTEXT);
await this.updateFavorites();
this.refreshElement(this.mFavoriteSession);
}
Expand Down
Loading