diff --git a/packages/zowe-explorer-api/__tests__/__unit__/tree/ZoweTreeNode.unit.test.ts b/packages/zowe-explorer-api/__tests__/__unit__/tree/ZoweTreeNode.unit.test.ts index 18ae225350..b813ee53e2 100644 --- a/packages/zowe-explorer-api/__tests__/__unit__/tree/ZoweTreeNode.unit.test.ts +++ b/packages/zowe-explorer-api/__tests__/__unit__/tree/ZoweTreeNode.unit.test.ts @@ -13,7 +13,17 @@ import * as vscode from "vscode"; import { ZoweTreeNode } from "../../../src/tree/ZoweTreeNode"; import { IZoweTreeNode } from "../../../src/tree/IZoweTreeNode"; import { imperative } from "@zowe/cli"; + describe("ZoweTreeNode", () => { + const innerProfile = { user: "apple", password: "banana" }; + const fakeProfile: imperative.IProfileLoaded = { + name: "amazingProfile", + profile: innerProfile, + message: "", + type: "zosmf", + failNotFound: true, + }; + const makeNode = ( name: string, collapseState: vscode.TreeItemCollapsibleState, @@ -46,8 +56,8 @@ describe("ZoweTreeNode", () => { it("getProfile should return profile of current node", () => { const node = makeNode("test", vscode.TreeItemCollapsibleState.None, undefined); - node.setProfileToChoice("myProfile" as unknown as imperative.IProfileLoaded); - expect(node.getProfile()).toBe("myProfile"); + node.setProfileToChoice(fakeProfile); + expect(node.getProfile().name).toBe("amazingProfile"); }); it("getProfile should return profile of parent node", () => { @@ -78,4 +88,28 @@ describe("ZoweTreeNode", () => { expect(node.getProfile()).toBeUndefined(); expect(node.getProfileName()).toBeUndefined(); }); + + it("setProfileToChoice should update properties on existing profile object", () => { + const node = makeNode("test", vscode.TreeItemCollapsibleState.None, undefined, undefined, { + ...fakeProfile, + }); + node.setProfileToChoice({ ...fakeProfile, profile: { host: "example.com", port: 443 } }); + expect(node.getProfile().profile?.port).toBeDefined(); + }); + + it("setProfileToChoice should update child nodes with the new profile", () => { + const node = makeNode("test", vscode.TreeItemCollapsibleState.Expanded, undefined); + node.setProfileToChoice({ ...fakeProfile, profile: { ...fakeProfile.profile, user: "banana" } }); + const nodeChild = makeNode("child", vscode.TreeItemCollapsibleState.None, undefined); + nodeChild.setProfileToChoice(node.getProfile()); + node.children = [nodeChild as any]; + const fsEntry = { + metadata: { + profile: node.getProfile(), + }, + }; + expect(node.getProfile().profile?.user).toBe("banana"); + expect(nodeChild.getProfile().profile?.user).toBe("banana"); + expect(fsEntry.metadata.profile.profile?.user).toBe("banana"); + }); }); diff --git a/packages/zowe-explorer-api/__tests__/__unit__/vscode/ZoweVsCodeExtension.unit.test.ts b/packages/zowe-explorer-api/__tests__/__unit__/vscode/ZoweVsCodeExtension.unit.test.ts index eb07c71dc7..4889a4927f 100644 --- a/packages/zowe-explorer-api/__tests__/__unit__/vscode/ZoweVsCodeExtension.unit.test.ts +++ b/packages/zowe-explorer-api/__tests__/__unit__/vscode/ZoweVsCodeExtension.unit.test.ts @@ -219,6 +219,7 @@ describe("ZoweVsCodeExtension", () => { mergeArgsForProfile: jest.fn().mockReturnValue({ knownArgs: [] }), }), refresh: jest.fn(), + updateProfilesArrays: jest.fn(), }; beforeEach(() => { @@ -375,7 +376,7 @@ describe("ZoweVsCodeExtension", () => { expect(loginSpy).not.toHaveBeenCalled(); expect(testSpy).not.toHaveBeenCalled(); expect(testCache.updateBaseProfileFileLogin).toHaveBeenCalledWith(baseProfile, updProfile, false); - expect(testNode.setProfileToChoice).toHaveBeenCalled(); + expect(testCache.updateProfilesArrays).toHaveBeenCalledWith(serviceProfile, testNode); quickPickMock.mockRestore(); }); }); diff --git a/packages/zowe-explorer-api/src/profiles/ProfilesCache.ts b/packages/zowe-explorer-api/src/profiles/ProfilesCache.ts index ae01f01fbe..a712592132 100644 --- a/packages/zowe-explorer-api/src/profiles/ProfilesCache.ts +++ b/packages/zowe-explorer-api/src/profiles/ProfilesCache.ts @@ -16,6 +16,7 @@ import { URL } from "url"; import * as zowe from "@zowe/cli"; import { ZoweExplorerApi } from "./ZoweExplorerApi"; +import { IZoweNodeType } from "../tree"; // TODO: find a home for constants export const CONTEXT_PREFIX = "_"; @@ -129,10 +130,11 @@ export class ProfilesCache { * Updates profile in allProfiles array and if default updates defaultProfileByType * * @param {string} profileLoaded + * @param {IZoweNodeType} profileNode * * @returns {void} */ - public updateProfilesArrays(profileLoaded: zowe.imperative.IProfileLoaded): void { + public updateProfilesArrays(profileLoaded: zowe.imperative.IProfileLoaded, profileNode?: IZoweNodeType): void { // update allProfiles array const promptedTypeIndex = this.allProfiles.findIndex( (profile) => profile?.type === profileLoaded?.type && profile?.name === profileLoaded?.name @@ -143,6 +145,7 @@ export class ProfilesCache { if (defaultProf?.name === profileLoaded?.name) { this.defaultProfileByType.set(profileLoaded?.type, profileLoaded); } + profileNode?.setProfileToChoice(profileLoaded); } /** diff --git a/packages/zowe-explorer-api/src/tree/ZoweTreeNode.ts b/packages/zowe-explorer-api/src/tree/ZoweTreeNode.ts index 27ef201553..f44f8ffa92 100644 --- a/packages/zowe-explorer-api/src/tree/ZoweTreeNode.ts +++ b/packages/zowe-explorer-api/src/tree/ZoweTreeNode.ts @@ -112,7 +112,8 @@ export class ZoweTreeNode extends vscode.TreeItem { * @param {imperative.IProfileLoaded} The profile you will set the node to use */ public setProfileToChoice(aProfile: imperative.IProfileLoaded): void { - this.profile = aProfile; + // Don't reassign profile if its already defined, as we want to keep the reference valid for other nodes + this.profile = Object.assign(this.profile ?? {}, aProfile); } /** * Sets the session for this node to the one chosen in parameters. diff --git a/packages/zowe-explorer-api/src/vscode/ZoweVsCodeExtension.ts b/packages/zowe-explorer-api/src/vscode/ZoweVsCodeExtension.ts index a1cc9337e7..dc50d282a3 100644 --- a/packages/zowe-explorer-api/src/vscode/ZoweVsCodeExtension.ts +++ b/packages/zowe-explorer-api/src/vscode/ZoweVsCodeExtension.ts @@ -112,7 +112,7 @@ export class ZoweVsCodeExtension { options: IPromptCredentialsOptions, apiRegister: ZoweExplorerApi.IApiRegisterClient ): Promise { - const cache = ZoweVsCodeExtension.profilesCache; + const cache = options.zeProfiles ?? ZoweVsCodeExtension.profilesCache; const profInfo = await cache.getProfileInfo(); const setSecure = options.secure ?? profInfo.isSecured(); @@ -228,23 +228,11 @@ export class ZoweVsCodeExtension { // If base profile already has a token type stored, then we check whether or not the connection details are the same (serviceProfile.profile.host === baseProfile.profile.host && serviceProfile.profile.port === baseProfile.profile.port); // If the connection details do not match, then we MUST forcefully store the token in the service profile - let profileToUpdate: imperative.IProfileLoaded; - if (connOk) { - profileToUpdate = baseProfile; - } else { - profileToUpdate = serviceProfile; - } + const profileToUpdate = connOk ? baseProfile : serviceProfile; await cache.updateBaseProfileFileLogin(profileToUpdate, updBaseProfile, !connOk); - const baseIndex = cache.allProfiles.findIndex((profile) => profile.name === profileToUpdate.name); - cache.allProfiles[baseIndex] = { ...profileToUpdate, profile: { ...profileToUpdate.profile, ...updBaseProfile } }; - - if (node) { - node.setProfileToChoice({ - ...node.getProfile(), - profile: { ...node.getProfile().profile, ...updBaseProfile }, - }); - } + serviceProfile.profile = { ...serviceProfile.profile, ...updBaseProfile }; + cache.updateProfilesArrays(serviceProfile, node); return true; } @@ -283,11 +271,9 @@ export class ZoweVsCodeExtension { await (zeRegister?.getCommonApi(serviceProfile).logout ?? Logout.apimlLogout)(updSession); const connOk = serviceProfile.profile.host === baseProfile.profile.host && serviceProfile.profile.port === baseProfile.profile.port; - if (connOk) { - await cache.updateBaseProfileFileLogout(baseProfile); - } else { - await cache.updateBaseProfileFileLogout(serviceProfile); - } + await cache.updateBaseProfileFileLogout(connOk ? baseProfile : serviceProfile); + serviceProfile.profile = { ...serviceProfile.profile, tokenType: undefined, tokenValue: undefined }; + cache.updateProfilesArrays(serviceProfile); } } diff --git a/packages/zowe-explorer-api/src/vscode/doc/IPromptCredentials.ts b/packages/zowe-explorer-api/src/vscode/doc/IPromptCredentials.ts index ac98b31f3d..8fbc5b7ad2 100644 --- a/packages/zowe-explorer-api/src/vscode/doc/IPromptCredentials.ts +++ b/packages/zowe-explorer-api/src/vscode/doc/IPromptCredentials.ts @@ -11,6 +11,7 @@ import { imperative } from "@zowe/cli"; import { InputBoxOptions, OpenDialogOptions } from "vscode"; +import { ProfilesCache } from "../../profiles"; export interface IPromptCredentialsCommonOptions { rePrompt?: boolean; @@ -23,6 +24,7 @@ export interface IPromptCredentialsOptions extends IPromptCredentialsCommonOptio sessionName?: string; sessionType?: string; secure?: boolean; + zeProfiles?: ProfilesCache; } export interface IPromptUserPassOptions extends IPromptCredentialsCommonOptions { diff --git a/packages/zowe-explorer/CHANGELOG.md b/packages/zowe-explorer/CHANGELOG.md index 8e9441547c..784e37e0db 100644 --- a/packages/zowe-explorer/CHANGELOG.md +++ b/packages/zowe-explorer/CHANGELOG.md @@ -17,6 +17,8 @@ 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 issue where Search operation did not prompt for credentials if profile contains expired token. [#2259](https://github.com/zowe/zowe-explorer-vscode/issues/2259) +- Fixed issue where inactive status was not displayed for profiles loaded from Global Config. [#3134](https://github.com/zowe/zowe-explorer-vscode/issues/3134) - Fixed an issue where clicking on a file in the Unix System Services tree caused the tree to abruptly change focus to the selected item. [#2486](https://github.com/zowe/zowe-explorer-vscode/issues/2486) - Fixed an issue where binary USS files were not fetched using the "Pull from Mainframe" context menu option. [#3355](https://github.com/zowe/zowe-explorer-vscode/issues/3355) - Fixed an issue with Auto Save where a failed UNIX file or data set save operation caused an infinite loop of save requests. [#2406](https://github.com/zowe/zowe-explorer-vscode/issues/2406), [#2627](https://github.com/zowe/zowe-explorer-vscode/issues/2627) diff --git a/packages/zowe-explorer/__mocks__/vscode.ts b/packages/zowe-explorer/__mocks__/vscode.ts index 992a04f39e..9f094e6e49 100644 --- a/packages/zowe-explorer/__mocks__/vscode.ts +++ b/packages/zowe-explorer/__mocks__/vscode.ts @@ -9,6 +9,31 @@ * */ +/** + * A location in the editor at which progress information can be shown. It depends on the + * location how progress is visually represented. + */ +export enum ProgressLocation { + /** + * Show progress for the source control viewlet, as overlay for the icon and as progress bar + * inside the viewlet (when visible). Neither supports cancellation nor discrete progress nor + * a label to describe the operation. + */ + SourceControl = 1, + + /** + * Show progress in the status bar of the editor. Neither supports cancellation nor discrete progress. + * Supports rendering of {@link ThemeIcon theme icons} via the `$()`-syntax in the progress label. + */ + Window = 10, + + /** + * Show progress as notification with an optional cancel button. Supports to show infinite and discrete + * progress but does not support rendering of icons. + */ + Notification = 15, +} + export enum ViewColumn { /** * A *symbolic* editor column representing the currently active column. This value diff --git a/packages/zowe-explorer/__tests__/__unit__/Profiles.extended.unit.test.ts b/packages/zowe-explorer/__tests__/__unit__/Profiles.extended.unit.test.ts index cf4cfd5252..0f2cf6ac6e 100644 --- a/packages/zowe-explorer/__tests__/__unit__/Profiles.extended.unit.test.ts +++ b/packages/zowe-explorer/__tests__/__unit__/Profiles.extended.unit.test.ts @@ -30,7 +30,7 @@ import * as vscode from "vscode"; import * as utils from "../../src/utils/ProfilesUtils"; import * as globals from "../../src/globals"; import * as zowe from "@zowe/cli"; -import { Gui, ProfilesCache, ZoweTreeNode, ZoweVsCodeExtension } from "@zowe/zowe-explorer-api"; +import { Gui, IProfileValidation, ProfilesCache, ZoweTreeNode, ZoweVsCodeExtension } from "@zowe/zowe-explorer-api"; import { Profiles } from "../../src/Profiles"; import { ZoweExplorerExtender } from "../../src/ZoweExplorerExtender"; import { ZoweExplorerApiRegister } from "../../../zowe-explorer/src/ZoweExplorerApiRegister"; @@ -46,7 +46,7 @@ jest.mock("child_process"); jest.mock("fs"); jest.mock("fs-extra"); -async function createGlobalMocks() { +function createGlobalMocks() { const newMocks = { log: zowe.imperative.Logger.getAppLogger(), mockShowInputBox: jest.fn(), @@ -73,8 +73,6 @@ async function createGlobalMocks() { Notification: 15, }; }), - withProgress: null, - mockCallback: null, mockUrlInfo: { valid: true, protocol: "https", @@ -82,14 +80,22 @@ async function createGlobalMocks() { port: 143, }, mockProfileInstance: null, - mockProfilesCache: null, mockConfigInstance: createConfigInstance(), mockConfigLoad: null, }; - newMocks.mockProfilesCache = new ProfilesCache(zowe.imperative.Logger.getAppLogger()); - newMocks.withProgress = jest.fn().mockImplementation((_progLocation, _callback) => { - return newMocks.mockCallback; + Object.defineProperty(vscode.window, "withProgress", { + value: jest.fn().mockImplementation((progLocation, callback) => { + const progress = { + report: jest.fn(), + }; + const token = { + isCancellationRequested: false, + onCancellationRequested: jest.fn(), + }; + return callback(progress, token); + }), + configurable: true, }); Object.defineProperty(vscode.window, "showInformationMessage", { @@ -128,8 +134,6 @@ async function createGlobalMocks() { value: newMocks.mockConfigurationTarget, configurable: true, }); - Object.defineProperty(vscode, "ProgressLocation", { value: newMocks.ProgressLocation, configurable: true }); - Object.defineProperty(vscode.window, "withProgress", { value: newMocks.withProgress, configurable: true }); Object.defineProperty(ZoweLogger, "error", { value: jest.fn(), configurable: true }); Object.defineProperty(ZoweLogger, "debug", { value: jest.fn(), configurable: true }); Object.defineProperty(ZoweLogger, "warn", { value: jest.fn(), configurable: true }); @@ -248,7 +252,7 @@ describe("Profiles Unit Test - Function createInstance", () => { }); it("Tests that createInstance catches error and logs it", async () => { - const globalMocks = await createGlobalMocks(); + const globalMocks = createGlobalMocks(); jest.spyOn(Profiles.prototype, "refresh").mockRejectedValueOnce(new Error("test error")); jest.spyOn(Gui, "errorMessage").mockResolvedValueOnce(""); const errorSpy = jest.spyOn(ZoweLogger, "error"); @@ -260,7 +264,7 @@ describe("Profiles Unit Test - Function createInstance", () => { }); describe("Profiles Unit Tests - Function createNewConnection for v1 Profiles", () => { - async function createBlockMocks(globalMocks) { + function createBlockMocks(globalMocks) { const newMocks = { testSchemas: newTestSchemas(), inputBox: createInputBox("input"), @@ -275,7 +279,7 @@ describe("Profiles Unit Tests - Function createNewConnection for v1 Profiles", ( }); it("Tests that createNewConnection fails if profileName is missing", async () => { - const globalMocks = await createGlobalMocks(); + const globalMocks = createGlobalMocks(); await Profiles.getInstance().createNewConnection(""); expect(globalMocks.mockShowInformationMessage.mock.calls.length).toBe(1); @@ -283,7 +287,7 @@ describe("Profiles Unit Tests - Function createNewConnection for v1 Profiles", ( }); it("Tests that createNewConnection fails if profileType is missing", async () => { - const globalMocks = await createGlobalMocks(); + const globalMocks = createGlobalMocks(); jest.spyOn(globalMocks.mockProfileInstance, "getProfileType").mockImplementation(undefined); await Profiles.getInstance().createNewConnection(globalMocks.testProfile.name, undefined); @@ -292,8 +296,8 @@ describe("Profiles Unit Tests - Function createNewConnection for v1 Profiles", ( }); it("Tests that createNewConnection fails if zOSMF URL is missing", async () => { - const globalMocks = await createGlobalMocks(); - const blockMocks = await createBlockMocks(globalMocks); + const globalMocks = createGlobalMocks(); + const blockMocks = createBlockMocks(globalMocks); jest.spyOn(globalMocks.mockProfileInstance, "getSchema").mockReturnValue(blockMocks.testSchemas); globalMocks.mockCreateInputBox.mockResolvedValue(undefined); @@ -304,8 +308,8 @@ describe("Profiles Unit Tests - Function createNewConnection for v1 Profiles", ( }); it("Tests that createNewConnection fails if user escapes create at username", async () => { - const globalMocks = await createGlobalMocks(); - const blockMocks = await createBlockMocks(globalMocks); + const globalMocks = createGlobalMocks(); + const blockMocks = createBlockMocks(globalMocks); jest.spyOn(globalMocks.mockProfileInstance, "getSchema").mockReturnValue(blockMocks.testSchemas); jest.spyOn(globalMocks.mockProfileInstance, "urlInfo").mockReturnValue(globalMocks.mockUrlInfo); @@ -316,8 +320,8 @@ describe("Profiles Unit Tests - Function createNewConnection for v1 Profiles", ( }); it("Tests that createNewConnection fails if user escapes create at password", async () => { - const globalMocks = await createGlobalMocks(); - const blockMocks = await createBlockMocks(globalMocks); + const globalMocks = createGlobalMocks(); + const blockMocks = createBlockMocks(globalMocks); jest.spyOn(globalMocks.mockProfileInstance, "getSchema").mockReturnValue(blockMocks.testSchemas); jest.spyOn(globalMocks.mockProfileInstance, "urlInfo").mockReturnValue(globalMocks.mockUrlInfo); @@ -329,8 +333,8 @@ describe("Profiles Unit Tests - Function createNewConnection for v1 Profiles", ( }); it("Tests that createNewConnection fails if user escapes create at rejectUnauthorized", async () => { - const globalMocks = await createGlobalMocks(); - const blockMocks = await createBlockMocks(globalMocks); + const globalMocks = createGlobalMocks(); + const blockMocks = createBlockMocks(globalMocks); jest.spyOn(globalMocks.mockProfileInstance, "getSchema").mockReturnValue(blockMocks.testSchemas); jest.spyOn(globalMocks.mockProfileInstance, "urlInfo").mockReturnValue(globalMocks.mockUrlInfo); @@ -343,8 +347,8 @@ describe("Profiles Unit Tests - Function createNewConnection for v1 Profiles", ( }); it("Tests that createNewConnection fails if profileName is a duplicate", async () => { - const globalMocks = await createGlobalMocks(); - const blockMocks = await createBlockMocks(globalMocks); + const globalMocks = createGlobalMocks(); + const blockMocks = createBlockMocks(globalMocks); jest.spyOn(globalMocks.mockProfileInstance, "getSchema").mockReturnValue(blockMocks.testSchemas); jest.spyOn(globalMocks.mockProfileInstance, "urlInfo").mockReturnValue(globalMocks.mockUrlInfo); @@ -357,8 +361,8 @@ describe("Profiles Unit Tests - Function createNewConnection for v1 Profiles", ( }); it("Tests that createNewConnection creates a new profile", async () => { - const globalMocks = await createGlobalMocks(); - const blockMocks = await createBlockMocks(globalMocks); + const globalMocks = createGlobalMocks(); + const blockMocks = createBlockMocks(globalMocks); jest.spyOn(utils.ProfilesUtils, "getCredentialManagerOverride").mockReturnValueOnce("@zowe/cli"); @@ -373,8 +377,8 @@ describe("Profiles Unit Tests - Function createNewConnection for v1 Profiles", ( }); it("Tests that createNewConnection throws an exception and shows a config error", async () => { - const globalMocks = await createGlobalMocks(); - const blockMocks = await createBlockMocks(globalMocks); + const globalMocks = createGlobalMocks(); + const blockMocks = createBlockMocks(globalMocks); const testError = new Error("saveProfile error"); const mockSaveProfile = jest.spyOn(ProfilesCache.prototype as any, "saveProfile").mockImplementationOnce(async (_values, _name, _type) => { @@ -397,8 +401,8 @@ describe("Profiles Unit Tests - Function createNewConnection for v1 Profiles", ( }); it("Tests that createNewConnection returns 'fake' if the port is undefined and portInfo() returns correct port", async () => { - const globalMocks = await createGlobalMocks(); - const blockMocks = await createBlockMocks(globalMocks); + const globalMocks = createGlobalMocks(); + const blockMocks = createBlockMocks(globalMocks); jest.spyOn(utils.ProfilesUtils, "getCredentialManagerOverride").mockReturnValueOnce("@zowe/cli"); const customURLInfo = { valid: true, @@ -421,8 +425,8 @@ describe("Profiles Unit Tests - Function createNewConnection for v1 Profiles", ( }); it("Tests that createNewConnection returns undefined if the port is undefined and portInfo() returns NaN", async () => { - const globalMocks = await createGlobalMocks(); - const blockMocks = await createBlockMocks(globalMocks); + const globalMocks = createGlobalMocks(); + const blockMocks = createBlockMocks(globalMocks); const customURLInfo = { valid: true, protocol: "https", @@ -444,8 +448,8 @@ describe("Profiles Unit Tests - Function createNewConnection for v1 Profiles", ( }); it("Tests that createNewConnection enters default case when encoding is present in schema and value is the number 0", async () => { - const globalMocks = await createGlobalMocks(); - const blockMocks = await createBlockMocks(globalMocks); + const globalMocks = createGlobalMocks(); + const blockMocks = createBlockMocks(globalMocks); jest.spyOn(utils.ProfilesUtils, "getCredentialManagerOverride").mockReturnValueOnce("@zowe/cli"); jest.spyOn(globalMocks.mockProfileInstance, "getSchema").mockReturnValueOnce(blockMocks.testSchemas); @@ -468,8 +472,8 @@ describe("Profiles Unit Tests - Function createNewConnection for v1 Profiles", ( }); it("Tests that createNewConnection enters default case when encoding is present in schema and value is NaN", async () => { - const globalMocks = await createGlobalMocks(); - const blockMocks = await createBlockMocks(globalMocks); + const globalMocks = createGlobalMocks(); + const blockMocks = createBlockMocks(globalMocks); jest.spyOn(utils.ProfilesUtils, "getCredentialManagerOverride").mockReturnValueOnce("@zowe/cli"); jest.spyOn(globalMocks.mockProfileInstance, "getSchema").mockReturnValueOnce(blockMocks.testSchemas); @@ -493,8 +497,8 @@ describe("Profiles Unit Tests - Function createNewConnection for v1 Profiles", ( }); it("Tests that createNewConnection enters default case when boolean is present in schema and returns undefined", async () => { - const globalMocks = await createGlobalMocks(); - const blockMocks = await createBlockMocks(globalMocks); + const globalMocks = createGlobalMocks(); + const blockMocks = createBlockMocks(globalMocks); jest.spyOn(globalMocks.mockProfileInstance, "getSchema").mockReturnValueOnce(blockMocks.testSchemas); jest.spyOn(globalMocks.mockProfileInstance, "urlInfo").mockReturnValueOnce(globalMocks.mockUrlInfo); @@ -521,8 +525,8 @@ describe("Profiles Unit Tests - Function createNewConnection for v1 Profiles", ( }); it("Tests that createNewConnection enters default case when string is present in schema and returns 'fake'", async () => { - const globalMocks = await createGlobalMocks(); - const blockMocks = await createBlockMocks(globalMocks); + const globalMocks = createGlobalMocks(); + const blockMocks = createBlockMocks(globalMocks); jest.spyOn(utils.ProfilesUtils, "getCredentialManagerOverride").mockReturnValueOnce("@zowe/cli"); jest.spyOn(globalMocks.mockProfileInstance, "getSchema").mockReturnValueOnce(blockMocks.testSchemas); @@ -551,7 +555,7 @@ describe("Profiles Unit Tests - Function createNewConnection for v1 Profiles", ( }); describe("Profiles Unit Tests - Function createZoweSession", () => { - async function createBlockMocks(globalMocks) { + function createBlockMocks(globalMocks) { const newMocks = { session: createISessionWithoutCredentials(), treeView: createTreeView(), @@ -566,8 +570,8 @@ describe("Profiles Unit Tests - Function createZoweSession", () => { return newMocks; } it("Tests that createZoweSession presents correct message when escaping selection of quickpick", async () => { - const globalMocks = await createGlobalMocks(); - const blockMocks = await createBlockMocks(globalMocks); + const globalMocks = createGlobalMocks(); + const blockMocks = createBlockMocks(globalMocks); const spy = jest.spyOn(Gui, "createQuickPick"); const spyDebug = jest.spyOn(ZoweLogger, "debug"); jest.spyOn(Gui, "resolveQuickPick").mockResolvedValueOnce(undefined); @@ -580,7 +584,7 @@ describe("Profiles Unit Tests - Function createZoweSession", () => { }); it("Tests that createZoweSession runs successfully", async () => { - const globalMocks = await createGlobalMocks(); + const globalMocks = createGlobalMocks(); const spyInfo = jest.spyOn(ZoweLogger, "info"); jest.spyOn(Gui, "createQuickPick").mockReturnValue({ show: jest.fn(), @@ -611,7 +615,7 @@ describe("Profiles Unit Tests - Function createZoweSession", () => { }); it("Tests that createZoweSession runs successfully and uses the chosenProfile", async () => { - const globalMocks = await createGlobalMocks(); + const globalMocks = createGlobalMocks(); jest.spyOn(Gui, "createQuickPick").mockReturnValue({ show: jest.fn(), hide: jest.fn(), @@ -626,7 +630,7 @@ describe("Profiles Unit Tests - Function createZoweSession", () => { }); it("Tests that createZoweSession catches error and logs it", async () => { - const globalMocks = await createGlobalMocks(); + const globalMocks = createGlobalMocks(); jest.spyOn(Gui, "createQuickPick").mockReturnValue({ show: jest.fn(), hide: jest.fn(), @@ -645,7 +649,7 @@ describe("Profiles Unit Tests - Function createZoweSession", () => { describe("Profiles Unit Tests - Function editZoweConfigFile", () => { it("Tests that editZoweConfigFile presents correct message when escaping selection of quickpick", async () => { - const globalMocks = await createGlobalMocks(); + const globalMocks = createGlobalMocks(); const spy = jest.spyOn(Gui, "showQuickPick"); spy.mockResolvedValueOnce(undefined); @@ -655,7 +659,7 @@ describe("Profiles Unit Tests - Function editZoweConfigFile", () => { spy.mockClear(); }); it("Tests that editZoweConfigFile opens correct file when Global is selected", async () => { - const globalMocks = await createGlobalMocks(); + const globalMocks = createGlobalMocks(); const spyQuickPick = jest.spyOn(Gui, "showQuickPick"); spyQuickPick.mockResolvedValueOnce("Global: in the Zowe home directory" as any); @@ -667,7 +671,7 @@ describe("Profiles Unit Tests - Function editZoweConfigFile", () => { spyOpenFile.mockClear(); }); it("Tests that editZoweConfigFile opens correct file when only Global config available", async () => { - const globalMocks = await createGlobalMocks(); + const globalMocks = createGlobalMocks(); globalMocks.mockConfigLoad.load.mockResolvedValueOnce({ layers: [ { @@ -685,7 +689,7 @@ describe("Profiles Unit Tests - Function editZoweConfigFile", () => { spyOpenFile.mockClear(); }); it("Tests that editZoweConfigFile opens correct file when Project is selected", async () => { - const globalMocks = await createGlobalMocks(); + const globalMocks = createGlobalMocks(); const spyQuickPick = jest.spyOn(Gui, "showQuickPick"); spyQuickPick.mockResolvedValueOnce("Project: in the current working directory" as any); @@ -697,7 +701,7 @@ describe("Profiles Unit Tests - Function editZoweConfigFile", () => { spyOpenFile.mockClear(); }); it("Tests that editZoweConfigFile opens correct file when only Project config available", async () => { - const globalMocks = await createGlobalMocks(); + const globalMocks = createGlobalMocks(); globalMocks.mockConfigLoad.load.mockResolvedValueOnce({ layers: [ { @@ -717,7 +721,7 @@ describe("Profiles Unit Tests - Function editZoweConfigFile", () => { }); describe("Profiles Unit Tests - Function createZoweSchema", () => { - async function createBlockMocks(globalMocks) { + function createBlockMocks(globalMocks) { const newMocks = { session: createISessionWithoutCredentials(), treeView: createTreeView(), @@ -741,8 +745,8 @@ describe("Profiles Unit Tests - Function createZoweSchema", () => { return newMocks; } it("Tests that createZoweSchema presents correct message when escaping selection of config location prompt", async () => { - const globalMocks = await createGlobalMocks(); - const blockMocks = await createBlockMocks(globalMocks); + const globalMocks = createGlobalMocks(); + const blockMocks = createBlockMocks(globalMocks); const spy = jest.spyOn(Gui, "showQuickPick"); spy.mockResolvedValueOnce(undefined); @@ -752,8 +756,8 @@ describe("Profiles Unit Tests - Function createZoweSchema", () => { spy.mockClear(); }); it("Tests that createZoweSchema will open correct config file when cancelling creation in location with existing config file", async () => { - const globalMocks = await createGlobalMocks(); - const blockMocks = await createBlockMocks(globalMocks); + const globalMocks = createGlobalMocks(); + const blockMocks = createBlockMocks(globalMocks); const spyQuickPick = jest.spyOn(Gui, "showQuickPick"); globalMocks.mockShowQuickPick.mockResolvedValueOnce("Global: in the Zowe home directory"); @@ -774,8 +778,8 @@ describe("Profiles Unit Tests - Function createZoweSchema", () => { spyOpenFile.mockClear(); }); it("Test that createZoweSchema will open config on error if error deals with parsing file", async () => { - const globalMocks = await createGlobalMocks(); - const blockMocks = await createBlockMocks(globalMocks); + const globalMocks = createGlobalMocks(); + const blockMocks = createBlockMocks(globalMocks); const spyQuickPick = jest.spyOn(Gui, "showQuickPick"); globalMocks.mockShowQuickPick.mockResolvedValueOnce("Global: in the Zowe home directory"); @@ -792,8 +796,8 @@ describe("Profiles Unit Tests - Function createZoweSchema", () => { spyZoweConfigError.mockClear(); }); it("Test that createZoweSchema will auto create global if VSC not in project and config doesn't exist", async () => { - const globalMocks = await createGlobalMocks(); - const blockMocks = await createBlockMocks(globalMocks); + const globalMocks = createGlobalMocks(); + const blockMocks = createBlockMocks(globalMocks); Object.defineProperty(vscode.workspace, "workspaceFolders", { value: undefined, configurable: true, @@ -820,8 +824,8 @@ describe("Profiles Unit Tests - Function createZoweSchema", () => { }); it("Tests that createZoweSchema will return the config file path", async () => { - const globalMocks = await createGlobalMocks(); - const blockMocks = await createBlockMocks(globalMocks); + const globalMocks = createGlobalMocks(); + const blockMocks = createBlockMocks(globalMocks); Object.defineProperty(globals, "ISTHEIA", { value: true, configurable: true }); Object.defineProperty(vscode.workspace, "workspaceFolders", { @@ -1102,15 +1106,14 @@ describe("Profiles Unit Tests - function promptCredentials", () => { profile: { user: "test", password: "12345", - base64EncodedAuth: "encodedAuth", } as zowe.imperative.IProfile, } as zowe.imperative.IProfileLoaded); jest.spyOn(Profiles.getInstance(), "updateProfilesArrays").mockImplementation(); - await expect(Profiles.getInstance().promptCredentials("secure_config_props")).resolves.toEqual(["test", "12345", "encodedAuth"]); + await expect(Profiles.getInstance().promptCredentials("secure_config_props")).resolves.toEqual(["test", "12345"]); }); it("Tests that promptCredentials catches error and logs it", async () => { - const globalMocks = await createGlobalMocks(); + const globalMocks = createGlobalMocks(); jest.spyOn(Profiles.getInstance(), "getProfileInfo").mockRejectedValueOnce(new Error("test error")); jest.spyOn(Gui, "errorMessage").mockResolvedValueOnce(""); const errorSpy = jest.spyOn(ZoweLogger, "error"); @@ -1196,23 +1199,30 @@ describe("Profiles Unit Tests - function getDeleteProfile", () => { }); describe("Profiles Unit Tests - function validateProfile", () => { - it("should return an object with profile validation status if validated profiles exist", async () => { + function createBlockMocks() { + createGlobalMocks(); + const newMocks = { + profilesForValidation: [] as IProfileValidation[], + getStatusSpy: jest.fn(), + }; + Object.defineProperty(Profiles.getInstance(), "profilesForValidation", { - value: [ - { - name: "test1", - message: "", - type: "", - status: "active", - failNotFound: false, - }, - ], + get: () => newMocks.profilesForValidation, configurable: true, }); - jest.spyOn(Gui, "withProgress").mockResolvedValue(undefined); - jest.spyOn(ZoweExplorerApiRegister.getInstance(), "getCommonApi").mockResolvedValueOnce({ - getStatus: () => "active", + jest.spyOn(ZoweExplorerApiRegister.getInstance(), "getCommonApi").mockReturnValueOnce({ + getStatus: newMocks.getStatusSpy, } as never); + + return newMocks; + } + + it("should return an object with profile validation status if validated profiles exist", async () => { + const blockMocks = createBlockMocks(); + blockMocks.profilesForValidation.push({ + name: "test1", + status: "active", + }); await expect( Profiles.getInstance().validateProfiles({ name: "test1", @@ -1224,16 +1234,11 @@ describe("Profiles Unit Tests - function validateProfile", () => { name: "test1", status: "active", }); + expect(blockMocks.getStatusSpy).not.toHaveBeenCalled(); }); it("should return an object with profile validation status of 'active' from session status if validated profiles does not exist", async () => { - Object.defineProperty(Profiles.getInstance(), "profilesForValidation", { - value: [], - configurable: true, - }); - jest.spyOn(Gui, "withProgress").mockResolvedValue("active"); - jest.spyOn(ZoweExplorerApiRegister.getInstance(), "getCommonApi").mockResolvedValueOnce({ - getStatus: () => "active", - } as never); + const blockMocks = createBlockMocks(); + blockMocks.getStatusSpy.mockResolvedValue("active"); await expect( Profiles.getInstance().validateProfiles({ name: "test1", @@ -1245,16 +1250,11 @@ describe("Profiles Unit Tests - function validateProfile", () => { name: "test1", status: "active", }); + expect(blockMocks.getStatusSpy).toHaveBeenCalledTimes(1); }); it("should return an object with profile validation status of 'inactive' from session status if validated profiles does not exist", async () => { - Object.defineProperty(Profiles.getInstance(), "profilesForValidation", { - value: [], - configurable: true, - }); - jest.spyOn(Gui, "withProgress").mockResolvedValue("inactive"); - jest.spyOn(ZoweExplorerApiRegister.getInstance(), "getCommonApi").mockResolvedValueOnce({ - getStatus: () => "inactive", - } as never); + const blockMocks = createBlockMocks(); + blockMocks.getStatusSpy.mockResolvedValue("inactive"); await expect( Profiles.getInstance().validateProfiles({ name: "test1", @@ -1266,36 +1266,25 @@ describe("Profiles Unit Tests - function validateProfile", () => { name: "test1", status: "inactive", }); + expect(blockMocks.getStatusSpy).toHaveBeenCalledTimes(1); }); it("should handle the error if call to getStatus fails", async () => { - Object.defineProperty(Profiles.getInstance(), "profilesForValidation", { - value: [], - configurable: true, - }); - const errorHandlingSpy = jest.spyOn(utils, "errorHandling"); + const blockMocks = createBlockMocks(); const testError = new Error("failed to validate profile"); - jest.spyOn(Gui, "withProgress").mockImplementation(() => { + blockMocks.getStatusSpy.mockImplementation(() => { throw testError; }); - jest.spyOn(ZoweExplorerApiRegister.getInstance(), "getCommonApi").mockResolvedValueOnce({ - getStatus: () => "inactive", - } as never); + const errorHandlingSpy = jest.spyOn(utils, "errorHandling"); await Profiles.getInstance().validateProfiles({ name: "test1", message: "", type: "", failNotFound: false, }); - expect(errorHandlingSpy).toBeCalledWith(testError, "test1"); + expect(errorHandlingSpy).toHaveBeenCalledWith(testError, "test1"); }); it("should return an object with profile validation status of 'unverified' from session status if validated profiles doesn't exist", async () => { - Object.defineProperty(Profiles.getInstance(), "profilesForValidation", { - value: [], - configurable: true, - }); - jest.spyOn(ZoweExplorerApiRegister.getInstance(), "getCommonApi").mockResolvedValueOnce({ - getStatus: undefined, - } as never); + createBlockMocks(); await expect( Profiles.getInstance().validateProfiles({ name: "test1", @@ -1312,7 +1301,7 @@ describe("Profiles Unit Tests - function validateProfile", () => { describe("Profiles Unit Tests - function deleteProfile", () => { it("should delete profile", async () => { - const globalMocks = await createGlobalMocks(); + const globalMocks = createGlobalMocks(); const datasetSessionNode = createDatasetSessionNode(globalMocks.testSession, globalMocks.testProfile); const datasetTree = createDatasetTree(datasetSessionNode, globalMocks.testProfile); @@ -1364,7 +1353,7 @@ describe("Profiles Unit Tests - function deleteProfile", () => { }); it("Tests that deleteProfile catches error and logs it", async () => { - const globalMocks = await createGlobalMocks(); + const globalMocks = createGlobalMocks(); const datasetSessionNode = createDatasetSessionNode(globalMocks.testSession, globalMocks.testProfile); const datasetTree = createDatasetTree(datasetSessionNode, globalMocks.testProfile); const ussSessionNode = [createUSSSessionNode(globalMocks.testSession, globalMocks.testProfile)]; @@ -1433,35 +1422,57 @@ describe("Profiles Unit Tests - function checkCurrentProfile", () => { jest.spyOn(Profiles.getInstance(), "getSecurePropsForProfile").mockResolvedValue([]); }; - it("should show as active in status of profile", async () => { - const globalMocks = await createGlobalMocks(); + it("should show as active in status of profile using basic auth", async () => { + const globalMocks = createGlobalMocks(); environmentSetup(globalMocks); setupProfilesCheck(globalMocks); - jest.spyOn(Profiles.getInstance(), "validateProfiles").mockReturnValue({ status: "active", name: "sestest" } as any); - jest.spyOn(Profiles.getInstance(), "promptCredentials").mockResolvedValue(["sestest", "12345", "base64Auth"]); + jest.spyOn(Profiles.getInstance(), "validateProfiles").mockResolvedValue({ status: "active", name: "sestest" }); + const promptCredentialsSpy = jest.spyOn(Profiles.getInstance(), "promptCredentials").mockResolvedValueOnce(["sestest", "12345"]); await expect(Profiles.getInstance().checkCurrentProfile(globalMocks.testProfile)).resolves.toEqual({ name: "sestest", status: "active" }); + expect(promptCredentialsSpy).toHaveBeenCalledTimes(1); }); - it("should show as unverified in status of profile", async () => { - const globalMocks = await createGlobalMocks(); + it("should show as active in status of profile using token auth", async () => { + const globalMocks = createGlobalMocks(); + jest.spyOn(utils.ProfilesUtils, "isUsingTokenAuth").mockResolvedValueOnce(true); environmentSetup(globalMocks); setupProfilesCheck(globalMocks); - jest.spyOn(Profiles.getInstance(), "promptCredentials").mockResolvedValue(undefined); + const ssoLoginSpy = jest.spyOn(Profiles.getInstance(), "ssoLogin").mockResolvedValueOnce(); + jest.spyOn(Profiles.getInstance(), "loadNamedProfile").mockReturnValueOnce(globalMocks.testProfile); + await expect(Profiles.getInstance().checkCurrentProfile(globalMocks.testProfile)).resolves.toEqual({ name: "sestest", status: "active" }); + expect(ssoLoginSpy).toHaveBeenCalledTimes(1); + }); + it("should show as unverified in status of profile", async () => { + const globalMocks = createGlobalMocks(); + setupProfilesCheck(globalMocks); + jest.spyOn(Profiles.getInstance(), "validateProfiles").mockResolvedValue({ status: "unverified", name: "sestest" }); await expect(Profiles.getInstance().checkCurrentProfile(globalMocks.testProfile)).resolves.toEqual({ name: "sestest", status: "unverified" }); }); it("should show as inactive in status of profile", async () => { - const globalMocks = await createGlobalMocks(); + const globalMocks = createGlobalMocks(); setupProfilesCheck(globalMocks); + jest.spyOn(Profiles.getInstance(), "validateProfiles").mockResolvedValue({ status: "inactive", name: "sestest" }); await expect(Profiles.getInstance().checkCurrentProfile(globalMocks.testProfile)).resolves.toEqual({ name: "sestest", status: "inactive" }); }); + it("should show as unverified if using basic auth and has expired password", async () => { + const globalMocks = createGlobalMocks(); + environmentSetup(globalMocks); + setupProfilesCheck(globalMocks); + const errorHandlingSpy = jest.spyOn(utils, "errorHandling").mockImplementation(); + jest.spyOn(Profiles.getInstance(), "promptCredentials").mockRejectedValueOnce(new Error("Failed to login")); + await expect(Profiles.getInstance().checkCurrentProfile(globalMocks.testProfile)).resolves.toEqual({ name: "sestest", status: "unverified" }); + expect(errorHandlingSpy).toHaveBeenCalledTimes(1); + }); it("should show as unverified if using token auth and is logged out or has expired token", async () => { - const globalMocks = await createGlobalMocks(); - jest.spyOn(utils, "errorHandling").mockImplementation(); - jest.spyOn(utils.ProfilesUtils, "isUsingTokenAuth").mockResolvedValueOnce(true); + const globalMocks = createGlobalMocks(); + environmentSetup(globalMocks); setupProfilesCheck(globalMocks); + const errorHandlingSpy = jest.spyOn(utils, "errorHandling").mockImplementation(); + jest.spyOn(utils.ProfilesUtils, "isUsingTokenAuth").mockResolvedValueOnce(true); await expect(Profiles.getInstance().checkCurrentProfile(globalMocks.testProfile)).resolves.toEqual({ name: "sestest", status: "unverified" }); + expect(errorHandlingSpy).toHaveBeenCalledTimes(1); }); it("should show as unverified if profiles fail to load", async () => { - const globalMocks = await createGlobalMocks(); + const globalMocks = createGlobalMocks(); jest.spyOn(Profiles.getInstance(), "getProfileInfo").mockRejectedValueOnce(new Error("test error")); jest.spyOn(Gui, "errorMessage").mockResolvedValueOnce(""); const errorSpy = jest.spyOn(ZoweLogger, "error"); @@ -1474,7 +1485,7 @@ describe("Profiles Unit Tests - function checkCurrentProfile", () => { describe("Profiles Unit Tests - function editSession", () => { it("should successfully return the edited session", async () => { - const globalMocks = await createGlobalMocks(); + const globalMocks = createGlobalMocks(); const testSchema = newTestSchemas(); testSchema["encoding"] = { type: "string", @@ -1541,7 +1552,7 @@ describe("Profiles Unit Tests - function editSession", () => { }); it("Tests that editSession catches error and logs it", async () => { - const globalMocks = await createGlobalMocks(); + const globalMocks = createGlobalMocks(); jest.spyOn(Profiles.getInstance(), "getProfileInfo").mockRejectedValueOnce(new Error("test error")); jest.spyOn(Gui, "errorMessage").mockResolvedValueOnce(""); const errorSpy = jest.spyOn(ZoweLogger, "error"); @@ -1554,7 +1565,7 @@ describe("Profiles Unit Tests - function editSession", () => { describe("Profiles Unit Tests - function getProfileSetting", () => { it("should retrive the profile with a status of unverified", async () => { - const globalMocks = await createGlobalMocks(); + const globalMocks = createGlobalMocks(); Object.defineProperty(Profiles.getInstance(), "profilesValidationSetting", { value: [ { @@ -1597,7 +1608,7 @@ describe("Profiles Unit Tests - function getProfileSetting", () => { describe("Profiles Unit Tests - function disableValidationContext", () => { it("should disable validation context and return updated node", async () => { - const globalMocks = await createGlobalMocks(); + const globalMocks = createGlobalMocks(); const spy = jest.spyOn(ZoweLogger, "trace"); const testNode = new (ZoweTreeNode as any)( "test", @@ -1624,7 +1635,7 @@ describe("Profiles Unit Tests - function disableValidationContext", () => { describe("Profiles Unit Tests - function enableValidationContext", () => { it("should enable validation context and return updated node", async () => { - const globalMocks = await createGlobalMocks(); + const globalMocks = createGlobalMocks(); const spy = jest.spyOn(ZoweLogger, "trace"); const testNode = new (ZoweTreeNode as any)( "test", @@ -1652,7 +1663,7 @@ describe("Profiles Unit Tests - function ssoLogin", () => { let testNode; let globalMocks; beforeEach(async () => { - globalMocks = await createGlobalMocks(); + globalMocks = createGlobalMocks(); testNode = new (ZoweTreeNode as any)( "fake", vscode.TreeItemCollapsibleState.None, @@ -1728,7 +1739,7 @@ describe("Profiles Unit Tests - function handleSwitchAuthentication", () => { }); beforeEach(async () => { - globalMocks = await createGlobalMocks(); + globalMocks = createGlobalMocks(); testNode = new (ZoweTreeNode as any)( "test", vscode.TreeItemCollapsibleState.None, @@ -2293,7 +2304,7 @@ describe("Profiles Unit Tests - function ssoLogout", () => { let testNode; let globalMocks; beforeEach(async () => { - globalMocks = await createGlobalMocks(); + globalMocks = createGlobalMocks(); testNode = new (ZoweTreeNode as any)( "fake", vscode.TreeItemCollapsibleState.None, @@ -2341,7 +2352,7 @@ describe("Profiles Unit Tests - function ssoLogout", () => { describe("Profiles Unit Tests - function updateBaseProfileFileLogin", () => { it("should update the property of mProfileInfo", async () => { const privateProfile = Profiles.getInstance() as any; - const globalMocks = await createGlobalMocks(); + const globalMocks = createGlobalMocks(); const updatePropertyMock = jest.fn(); jest.spyOn(privateProfile, "getProfileInfo").mockReturnValue({ isSecured: () => true, @@ -2355,7 +2366,7 @@ describe("Profiles Unit Tests - function updateBaseProfileFileLogin", () => { describe("Profiles Unit Tests - function updateBaseProfileFileLogout", () => { it("should update the property of mProfileInfo", async () => { const privateProfile = Profiles.getInstance() as any; - const globalMocks = await createGlobalMocks(); + const globalMocks = createGlobalMocks(); const updateKnownPropertyMock = jest.fn(); jest.spyOn(privateProfile, "getProfileInfo").mockReturnValue({ isSecured: () => true, @@ -2374,7 +2385,7 @@ describe("Profiles Unit Tests - function updateBaseProfileFileLogout", () => { describe("Profiles Unit Tests - function createNonSecureProfile", () => { it("should create an unsecured profile by removing secure arrays and setting autoStore to false", async () => { - const globalMocks = await createGlobalMocks(); + const globalMocks = createGlobalMocks(); const privateProfile = Profiles.getInstance() as any; jest.spyOn(SettingsConfig, "getDirectValue").mockReturnValueOnce(false); expect(privateProfile.createNonSecureProfile(globalMocks.testTeamConfigProfile)).toEqual(undefined); @@ -2384,7 +2395,7 @@ describe("Profiles Unit Tests - function createNonSecureProfile", () => { describe("Profiles Unit Tests - function updateProfile", () => { it("should throw an error when getting the CliManager", async () => { - const globalMocks = await createGlobalMocks(); + const globalMocks = createGlobalMocks(); const privateProfile = Profiles.getInstance() as any; jest.spyOn(Profiles.getInstance(), "getCliProfileManager").mockReturnValue({ load: () => globalMocks.testProfile, @@ -2408,7 +2419,7 @@ describe("Profiles Unit Tests - function updateProfile", () => { describe("Profiles Unit Tests - function validationArraySetup", () => { it("should setup the validation array", async () => { - const globalMocks = await createGlobalMocks(); + const globalMocks = createGlobalMocks(); Object.defineProperty(Profiles.getInstance(), "profilesValidationSetting", { value: [ { @@ -2464,7 +2475,7 @@ describe("Profiles Unit Tests - function getSecurePropsForProfile", () => { jest.resetAllMocks(); }); it("should retrieve the secure properties of a profile", async () => { - const globalMocks = await createGlobalMocks(); + const globalMocks = createGlobalMocks(); jest.spyOn(Profiles.getInstance(), "getProfileInfo").mockResolvedValue({ mergeArgsForProfile: () => ({ knownArgs: [ @@ -2489,7 +2500,7 @@ describe("Profiles Unit Tests - function clearFilterFromAllTrees", () => { }); it("should fail to clear filter if no session nodes are available", async () => { - const globalMocks = await createGlobalMocks(); + const globalMocks = createGlobalMocks(); const testNode = new (ZoweTreeNode as any)( "fake", vscode.TreeItemCollapsibleState.None, @@ -2516,7 +2527,7 @@ describe("Profiles Unit Tests - function clearFilterFromAllTrees", () => { }); it("should fail to clear filters if the session node is not listed in the tree", async () => { - const globalMocks = await createGlobalMocks(); + const globalMocks = createGlobalMocks(); const testNode = new (ZoweTreeNode as any)( "fake", vscode.TreeItemCollapsibleState.None, @@ -2553,7 +2564,7 @@ describe("Profiles Unit Tests - function disableValidation", () => { }); it("should disable validation for the profile on all trees", async () => { - const globalMocks = await createGlobalMocks(); + const globalMocks = createGlobalMocks(); jest.spyOn(TreeProviders, "getSessionForAllTrees").mockReturnValue([globalMocks.testNode]); expect(globalMocks.testNode.contextValue).toEqual(globals.DS_SESSION_CONTEXT); expect(Profiles.getInstance().disableValidation(globalMocks.testNode)).toEqual(globalMocks.testNode); @@ -2561,7 +2572,7 @@ describe("Profiles Unit Tests - function disableValidation", () => { }); it("should disable validation for the profile on the current tree", async () => { - const globalMocks = await createGlobalMocks(); + const globalMocks = createGlobalMocks(); jest.spyOn(TreeProviders, "getSessionForAllTrees").mockReturnValue([globalMocks.testNode]); const disableValidationContextSpy = jest.spyOn(Profiles.getInstance(), "disableValidationContext"); expect(globalMocks.testNode.contextValue).toEqual(globals.DS_SESSION_CONTEXT); @@ -2579,7 +2590,7 @@ describe("Profiles Unit Tests - function enableValidation", () => { }); it("should enable validation for the profile on all trees", async () => { - const globalMocks = await createGlobalMocks(); + const globalMocks = createGlobalMocks(); jest.spyOn(TreeProviders, "getSessionForAllTrees").mockReturnValue([ createMockNode("test2", globals.DS_SESSION_CONTEXT), globalMocks.testNode, @@ -2590,7 +2601,7 @@ describe("Profiles Unit Tests - function enableValidation", () => { }); it("should enable validation for the profile on the current tree", async () => { - const globalMocks = await createGlobalMocks(); + const globalMocks = createGlobalMocks(); const enableValidationContextSpy = jest.spyOn(Profiles.getInstance(), "enableValidationContext"); jest.spyOn(TreeProviders, "getSessionForAllTrees").mockReturnValue([globalMocks.testNode]); expect(globalMocks.testNode.contextValue).toEqual(globals.DS_SESSION_CONTEXT); diff --git a/packages/zowe-explorer/__tests__/__unit__/dataset/actions.unit.test.ts b/packages/zowe-explorer/__tests__/__unit__/dataset/actions.unit.test.ts index 7b019df14b..9ec5d2c845 100644 --- a/packages/zowe-explorer/__tests__/__unit__/dataset/actions.unit.test.ts +++ b/packages/zowe-explorer/__tests__/__unit__/dataset/actions.unit.test.ts @@ -522,13 +522,11 @@ describe("Dataset Actions Unit Tests - Function deleteDatasetPrompt", () => { mocked(vscode.window.withProgress).mockImplementation((progLocation, callback) => { const progress = { - report: (message) => { - return; - }, + report: jest.fn(), }; const token = { isCancellationRequested: false, - onCancellationRequested: undefined, + onCancellationRequested: jest.fn(), }; return callback(progress, token); }); diff --git a/packages/zowe-explorer/__tests__/__unit__/mvs/mvsNodeActions.unit.test.ts b/packages/zowe-explorer/__tests__/__unit__/mvs/mvsNodeActions.unit.test.ts index ca1176cf84..4afb2b1286 100644 --- a/packages/zowe-explorer/__tests__/__unit__/mvs/mvsNodeActions.unit.test.ts +++ b/packages/zowe-explorer/__tests__/__unit__/mvs/mvsNodeActions.unit.test.ts @@ -43,17 +43,14 @@ async function createGlobalMocks() { Object.defineProperty(vscode.window, "showInformationMessage", { value: newMocks.showInformationMessage, configurable: true }); Object.defineProperty(vscode.workspace, "openTextDocument", { value: newMocks.openTextDocument, configurable: true }); Object.defineProperty(vscode, "ProgressLocation", { value: jest.fn(), configurable: true }); - Object.defineProperty(vscode.window, "withProgress", { value: jest.fn(), configurable: true }); Object.defineProperty(vscode.window, "withProgress", { value: jest.fn().mockImplementation((progLocation, callback) => { const progress = { - report: (message) => { - return; - }, + report: jest.fn(), }; const token = { isCancellationRequested: false, - onCancellationRequested: undefined, + onCancellationRequested: jest.fn(), }; return callback(progress, token); }), @@ -186,22 +183,6 @@ describe("mvsNodeActions", () => { testTree.getTreeView.mockReturnValueOnce(createTreeView()); const fileUri = { fsPath: "/tmp/foo" }; globalMocks.showOpenDialog.mockReturnValueOnce([fileUri]); - Object.defineProperty(vscode.window, "withProgress", { - value: jest.fn().mockImplementation((progLocation, callback) => { - const progress = { - report: (message) => { - return; - }, - }; - const token = { - isCancellationRequested: true, - onCancellationRequested: undefined, - }; - return callback(progress, token); - }), - configurable: true, - }); - await dsActions.uploadDialog(node, testTree); expect(globalMocks.showOpenDialog).toBeCalled(); diff --git a/packages/zowe-explorer/__tests__/__unit__/shared/actions.unit.test.ts b/packages/zowe-explorer/__tests__/__unit__/shared/actions.unit.test.ts index 7ca4692be8..9d8ef68e8d 100644 --- a/packages/zowe-explorer/__tests__/__unit__/shared/actions.unit.test.ts +++ b/packages/zowe-explorer/__tests__/__unit__/shared/actions.unit.test.ts @@ -50,13 +50,11 @@ async function createGlobalMocks() { Object.defineProperty(vscode.window, "withProgress", { value: jest.fn().mockImplementation((progLocation, callback) => { const progress = { - report: (message) => { - return; - }, + report: jest.fn(), }; const token = { isCancellationRequested: false, - onCancellationRequested: undefined, + onCancellationRequested: jest.fn(), }; return callback(progress, token); }), diff --git a/packages/zowe-explorer/__tests__/__unit__/uss/actions.unit.test.ts b/packages/zowe-explorer/__tests__/__unit__/uss/actions.unit.test.ts index 8451f82002..253a482879 100644 --- a/packages/zowe-explorer/__tests__/__unit__/uss/actions.unit.test.ts +++ b/packages/zowe-explorer/__tests__/__unit__/uss/actions.unit.test.ts @@ -616,13 +616,11 @@ describe("USS Action Unit Tests - Functions uploadDialog & uploadFile", () => { Object.defineProperty(vscode.window, "withProgress", { value: jest.fn().mockImplementation((progLocation, callback) => { const progress = { - report: (message) => { - return; - }, + report: jest.fn(), }; const token = { isCancellationRequested: false, - onCancellationRequested: undefined, + onCancellationRequested: jest.fn(), }; return callback(progress, token); }), diff --git a/packages/zowe-explorer/i18n/sample/src/Profiles.i18n.json b/packages/zowe-explorer/i18n/sample/src/Profiles.i18n.json index b48b0f2a1b..057a65ee48 100644 --- a/packages/zowe-explorer/i18n/sample/src/Profiles.i18n.json +++ b/packages/zowe-explorer/i18n/sample/src/Profiles.i18n.json @@ -1,8 +1,6 @@ { "profiles.operation.cancelled": "Operation Cancelled", "profiles.manualEditMsg": "The Team configuration file has been opened in the editor. Editing or removal of profiles will need to be done manually.", - "checkCurrentProfile.tokenAuthError.msg": "Token auth error", - "checkCurrentProfile.tokenAuthError.additionalDetails": "Profile was found using token auth, please log in to continue.", "profiles.createNewConnection": "$(plus) Create a new connection to z/OS", "profiles.createTeamConfig": "$(plus) Create a new Team Configuration file", "profiles.editConfig": "$(pencil) Edit Team Configuration file", diff --git a/packages/zowe-explorer/src/Profiles.ts b/packages/zowe-explorer/src/Profiles.ts index 1b3058c31b..8a1f0725ed 100644 --- a/packages/zowe-explorer/src/Profiles.ts +++ b/packages/zowe-explorer/src/Profiles.ts @@ -103,34 +103,34 @@ export class Profiles extends ProfilesCache { public async checkCurrentProfile(theProfile: zowe.imperative.IProfileLoaded): Promise { ZoweLogger.trace("Profiles.checkCurrentProfile called."); - let profileStatus: IProfileValidation; + let profileStatus: IProfileValidation = { name: theProfile.name, status: "unverified" }; let usingTokenAuth: boolean; try { usingTokenAuth = await ProfilesUtils.isUsingTokenAuth(theProfile.name); } catch (err) { ZoweLogger.error(err); ZoweExplorerExtender.showZoweConfigError(err.message); - return { name: theProfile.name, status: "unverified" }; - } - - if (usingTokenAuth && !theProfile.profile.tokenType) { - const error = new zowe.imperative.ImperativeError({ - msg: localize("checkCurrentProfile.tokenAuthError.msg", "Token auth error"), - additionalDetails: localize( - "checkCurrentProfile.tokenAuthError.additionalDetails", - "Profile was found using token auth, please log in to continue." - ), - errorCode: `${zowe.imperative.RestConstants.HTTP_STATUS_401}`, - }); - await errorHandling(error, theProfile.name, error.message); - profileStatus = { name: theProfile.name, status: "unverified" }; return profileStatus; } - if (!usingTokenAuth && (!theProfile.profile.user || !theProfile.profile.password)) { + if (usingTokenAuth && !theProfile.profile.tokenValue) { + ZoweLogger.debug(`Profile ${theProfile.name} is using token auth, prompting for missing credentials`); // The profile will need to be reactivated, so remove it from profilesForValidation this.profilesForValidation = this.profilesForValidation.filter( - (profile) => profile.status === "unverified" && profile.name !== theProfile.name + (profile) => !(profile.name === theProfile.name && profile.status !== "unverified") + ); + try { + await Profiles.getInstance().ssoLogin(null, theProfile.name); + theProfile = Profiles.getInstance().loadNamedProfile(theProfile.name); + } catch (error) { + await errorHandling(error, theProfile.name, error.message); + return profileStatus; + } + } else if (!usingTokenAuth && (!theProfile.profile.user || !theProfile.profile.password)) { + ZoweLogger.debug(`Profile ${theProfile.name} is using basic auth, prompting for missing credentials`); + // The profile will need to be reactivated, so remove it from profilesForValidation + this.profilesForValidation = this.profilesForValidation.filter( + (profile) => !(profile.name === theProfile.name && profile.status !== "unverified") ); let values: string[]; try { @@ -142,17 +142,12 @@ export class Profiles extends ProfilesCache { if (values) { theProfile.profile.user = values[0]; theProfile.profile.password = values[1]; - theProfile.profile.base64EncodedAuth = values[2]; - - // Validate profile - profileStatus = await this.getProfileSetting(theProfile); - } else { - profileStatus = { name: theProfile.name, status: "unverified" }; } - } else { - // Profile should have enough information to allow validation - profileStatus = await this.getProfileSetting(theProfile); } + + // Profile should have enough information to allow validation + profileStatus = await this.getProfileSetting(theProfile); + switch (profileStatus.status) { case "unverified": this.validProfile = ValidProfileEnum.UNVERIFIED; @@ -966,6 +961,7 @@ export class Profiles extends ProfilesCache { secure: mProfileInfo.isSecured(), userInputBoxOptions, passwordInputBoxOptions, + zeProfiles: this, }, ZoweExplorerApiRegister.getInstance() ); @@ -1163,7 +1159,7 @@ export class Profiles extends ProfilesCache { ZoweLogger.trace("Profiles.validateProfiles called."); let filteredProfile: IProfileValidation; let profileStatus; - const getSessStatus = await ZoweExplorerApiRegister.getInstance().getCommonApi(theProfile); + const getSessStatus = ZoweExplorerApiRegister.getInstance().getCommonApi(theProfile); // Check if the profile is already validated as active const desiredProfile = this.profilesForValidation.find((profile) => profile.name === theProfile.name && profile.status === "active"); @@ -1254,7 +1250,7 @@ export class Profiles extends ProfilesCache { const zeInstance = ZoweExplorerApiRegister.getInstance(); try { - loginTokenType = await zeInstance.getCommonApi(serviceProfile).getTokenTypeName(); + loginTokenType = zeInstance.getCommonApi(serviceProfile).getTokenTypeName(); } catch (error) { ZoweLogger.warn(error); Gui.showMessage(localize("ssoLogin.tokenType.error", "Error getting supported tokenType value for profile {0}", serviceProfile.name)); @@ -1508,9 +1504,7 @@ export class Profiles extends ProfilesCache { serviceProfile.profile != null && !serviceProfile.profile.tokenType?.startsWith(zowe.imperative.SessConstants.TOKEN_TYPE_APIML) ) { - await ZoweExplorerApiRegister.getInstance() - .getCommonApi(serviceProfile) - .logout(await node.getSession()); + await ZoweExplorerApiRegister.getInstance().getCommonApi(serviceProfile).logout(node.getSession()); } else { await ZoweVsCodeExtension.logoutWithBaseProfile(serviceProfile, ZoweExplorerApiRegister.getInstance(), this); } @@ -1566,14 +1560,7 @@ export class Profiles extends ProfilesCache { session.ISession.user = creds[0]; session.ISession.password = creds[1]; await ZoweExplorerApiRegister.getInstance().getCommonApi(serviceProfile).login(session); - const profIndex = this.allProfiles.findIndex((profile) => profile.name === serviceProfile.name); - this.allProfiles[profIndex] = { ...serviceProfile, profile: { ...serviceProfile, ...session } }; - if (node) { - node.setProfileToChoice({ - ...node.getProfile(), - profile: { ...node.getProfile().profile, ...session }, - }); - } + Profiles.getInstance().updateProfilesArrays(serviceProfile, node); return true; } diff --git a/packages/zowe-explorer/src/generators/icons/items/home.ts b/packages/zowe-explorer/src/generators/icons/items/home.ts index 0eb8a3aa27..5a0ec34f4c 100644 --- a/packages/zowe-explorer/src/generators/icons/items/home.ts +++ b/packages/zowe-explorer/src/generators/icons/items/home.ts @@ -11,13 +11,13 @@ import { IconHierarchyType, IconId, IIconItem } from "../index"; import { getIconPathInResources } from "../../../shared/utils"; -import { isHomeProfile } from "../../../shared/context"; +import { isHomeProfile, isSessionInactive } from "../../../shared/context"; const icon: IIconItem = { id: IconId.home, type: IconHierarchyType.base, path: getIconPathInResources("home.svg"), - check: (node) => isHomeProfile(node), + check: (node) => isHomeProfile(node) && !isSessionInactive(node), }; export default icon;