Skip to content

Commit

Permalink
Port fix for duplicate config watcher events
Browse files Browse the repository at this point in the history
Signed-off-by: Timothy Johnson <[email protected]>
  • Loading branch information
t1m0thyj committed Dec 27, 2024
1 parent cf55928 commit ad31c96
Show file tree
Hide file tree
Showing 15 changed files with 249 additions and 230 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
"vscode": "^1.53.2"
},
"dependencies": {
"@zowe/cli": "^7.29.7",
"@zowe/cli": "^7.29.8",
"vscode-nls": "^4.1.2"
},
"devDependencies": {
Expand Down
2 changes: 1 addition & 1 deletion packages/zowe-explorer-api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
},
"dependencies": {
"@types/vscode": "^1.53.2",
"@zowe/cli": "^7.29.7",
"@zowe/cli": "^7.29.8",
"@zowe/secrets-for-zowe-sdk": "^7.18.6",
"mustache": "^4.2.0",
"semver": "^7.5.3"
Expand Down
1 change: 1 addition & 0 deletions packages/zowe-explorer/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ All notable changes to the "vscode-extension-for-zowe" extension will be documen
- 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)
- Fixed an issue where editing a team config file or updating credentials in OS vault could trigger multiple events for a single action. [#3296](https://github.com/zowe/zowe-explorer-vscode/pull/3296)

## `2.18.0`

Expand Down
4 changes: 4 additions & 0 deletions packages/zowe-explorer/__mocks__/@zowe/imperative.ts
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,10 @@ export class ProfileInfo {
return;
}

public profileManagerWillLoad(): boolean {
return true;
}

public addProfileTypeToSchema(
profileType: string,
typeInfo: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,7 @@ describe("ZoweExplorerExtender unit tests", () => {

const readProfilesFromDiskSpy = jest.fn();
const refreshProfilesQueueAddSpy = jest.spyOn((ZoweExplorerExtender as any).refreshProfilesQueue, "add");
jest.spyOn(ProfilesUtils, "getProfileInfo").mockReturnValueOnce({
jest.spyOn(ProfilesUtils, "setupProfileInfo").mockReturnValueOnce({
readProfilesFromDisk: readProfilesFromDiskSpy,
} as any);
await expect(blockMocks.instTest.initForZowe("USS", ["" as any])).resolves.not.toThrow();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import { ZoweSaveQueue } from "../../../src/abstract/ZoweSaveQueue";
import { ZoweExplorerApiRegister } from "../../../src/ZoweExplorerApiRegister";
import * as HistoryView from "../../../src/shared/HistoryView";
import * as certWizard from "../../../src/utils/CertificateWizard";
import * as sharedUtils from "../../../src/shared/utils";

describe("Test src/shared/extension", () => {
describe("registerCommonCommands", () => {
Expand Down Expand Up @@ -326,6 +327,7 @@ describe("Test src/shared/extension", () => {
Object.defineProperty(globals, "SAVED_PROFILE_CONTENTS", { value: "test", configurable: true });
jest.spyOn(vscode.workspace, "createFileSystemWatcher").mockReturnValue(watcher);
jest.spyOn(ZoweExplorerApiRegister.getInstance().onProfilesUpdateEmitter, "fire").mockImplementation(mockEmitter);
jest.spyOn(sharedUtils, "debounce").mockImplementation((cb: any) => cb);
});

afterAll(() => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1355,3 +1355,33 @@ describe("Shared utils unit tests - function initializeFileOpening", () => {
expect(globalMocks.mockShowTextDocument).toBeCalledWith(globalMocks.mockTextDocument, { preview: false });
});
});

describe("Shared utils unit tests - function debounce", () => {
beforeAll(() => {
jest.useFakeTimers();
});

afterAll(() => {
jest.useRealTimers();
});

it("executes a function twice when time between calls is long", () => {
const mockEventHandler = jest.fn();
const debouncedFn = sharedUtils.debounce(mockEventHandler, 100);
debouncedFn();
jest.runAllTimers();
debouncedFn();
jest.runAllTimers();
expect(mockEventHandler).toHaveBeenCalledTimes(2);
});

it("executes a function only once when time between calls is short", () => {
const mockEventHandler = jest.fn();
const debouncedFn = sharedUtils.debounce(mockEventHandler, 100);
debouncedFn();
jest.advanceTimersByTime(10);
debouncedFn();
jest.runAllTimers();
expect(mockEventHandler).toHaveBeenCalledTimes(1);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -277,19 +277,20 @@ describe("ProfilesUtils unit tests", () => {
});

describe("readConfigFromDisk", () => {
it("should readConfigFromDisk and log 'Not Available'", async () => {
Object.defineProperty(vscode.workspace, "workspaceFolders", {
value: [
{
uri: {
fsPath: "./test",
},
Object.defineProperty(vscode.workspace, "workspaceFolders", {
value: [
{
uri: {
fsPath: "./test",
},
],
configurable: true,
});
},
],
configurable: true,
});

it("should readConfigFromDisk and find default profiles", async () => {
const mockReadProfilesFromDisk = jest.fn();
const profInfoSpy = jest.spyOn(profUtils.ProfilesUtils, "getProfileInfo").mockReturnValue({
jest.spyOn(profUtils.ProfilesUtils, "setupProfileInfo").mockReturnValueOnce({
readProfilesFromDisk: mockReadProfilesFromDisk,
usingTeamConfig: true,
getTeamConfig: () => ({
Expand All @@ -309,66 +310,48 @@ describe("ProfilesUtils unit tests", () => {
],
}),
} as never);
Object.defineProperty(globals.LOG, "debug", {
value: jest.fn(),
configurable: true,
});
const loggerSpy = jest.spyOn(ZoweLogger, "debug");
await expect(profUtils.ProfilesUtils.readConfigFromDisk()).resolves.not.toThrow();
expect(mockReadProfilesFromDisk).toHaveBeenCalledTimes(1);
profInfoSpy.mockRestore();
expect(loggerSpy).toHaveBeenLastCalledWith(expect.stringContaining(`Path: test, Found with the following defaults: "test"`));
});

it("should readConfigFromDisk and find with defaults", async () => {
Object.defineProperty(vscode.workspace, "workspaceFolders", {
value: [
{
uri: {
fsPath: "./test",
},
},
],
configurable: true,
});
it("should readConfigFromDisk and log 'Not Available'", async () => {
const mockReadProfilesFromDisk = jest.fn();
const profInfoSpy = jest.spyOn(profUtils.ProfilesUtils, "getProfileInfo").mockReturnValue({
jest.spyOn(profUtils.ProfilesUtils, "setupProfileInfo").mockResolvedValueOnce({
readProfilesFromDisk: mockReadProfilesFromDisk,
usingTeamConfig: true,
getTeamConfig: () => [],
getTeamConfig: () => ({
exists: true,
layers: [
{
path: "test",
exists: false,
properties: {},
},
],
}),
} as never);
Object.defineProperty(globals.LOG, "debug", {
value: jest.fn(),
configurable: true,
});
const loggerSpy = jest.spyOn(ZoweLogger, "debug");
await expect(profUtils.ProfilesUtils.readConfigFromDisk()).resolves.not.toThrow();
expect(mockReadProfilesFromDisk).toHaveBeenCalledTimes(1);
profInfoSpy.mockRestore();
expect(loggerSpy).toHaveBeenLastCalledWith(expect.stringContaining("Path: test, Not available"));
});

it("should keep Imperative error details if readConfigFromDisk fails", async () => {
Object.defineProperty(vscode.workspace, "workspaceFolders", {
value: [
{
uri: {
fsPath: "./test",
},
},
],
configurable: true,
});
const impErr = new zowe.imperative.ImperativeError({ msg: "Unexpected Imperative error" });
const mockReadProfilesFromDisk = jest.fn().mockRejectedValue(impErr);
const profInfoSpy = jest.spyOn(profUtils.ProfilesUtils, "getProfileInfo").mockReturnValue({
jest.spyOn(profUtils.ProfilesUtils, "setupProfileInfo").mockResolvedValueOnce({
readProfilesFromDisk: mockReadProfilesFromDisk,
usingTeamConfig: true,
getTeamConfig: () => [],
} as never);
await expect(profUtils.ProfilesUtils.readConfigFromDisk()).rejects.toBe(impErr);
expect(mockReadProfilesFromDisk).toHaveBeenCalledTimes(1);
profInfoSpy.mockRestore();
});

it("should warn the user when using team config with a missing schema", async () => {
const profInfoSpy = jest.spyOn(profUtils.ProfilesUtils, "getProfileInfo").mockReturnValueOnce({
jest.spyOn(profUtils.ProfilesUtils, "setupProfileInfo").mockResolvedValueOnce({
readProfilesFromDisk: jest.fn(),
usingTeamConfig: true,
hasValidSchema: false,
Expand All @@ -394,7 +377,6 @@ describe("ProfilesUtils unit tests", () => {
expect(warnMsgSpy).toHaveBeenCalledWith(
"No valid schema was found for the active team configuration. This may introduce issues with profiles in Zowe Explorer."
);
profInfoSpy.mockRestore();
});
});

Expand Down Expand Up @@ -736,14 +718,14 @@ describe("ProfilesUtils unit tests", () => {
});
});

describe("getProfilesInfo", () => {
describe("setupProfileInfo", () => {
let isVSCodeCredentialPluginInstalledSpy: jest.SpyInstance;
let getDirectValueSpy: jest.SpyInstance;
let fetchRegisteredPluginsSpy: jest.SpyInstance;
let getCredentialManagerOverrideSpy: jest.SpyInstance;
let getCredentialManagerMapSpy: jest.SpyInstance;
let setupCustomCredentialManagerSpy: jest.SpyInstance;
let readProfilesFromDiskSpy: jest.SpyInstance;
let profileManagerWillLoadSpy: jest.SpyInstance;
let promptAndDisableCredentialManagementSpy: jest.SpyInstance;

beforeEach(() => {
Expand All @@ -756,7 +738,7 @@ describe("ProfilesUtils unit tests", () => {
getCredentialManagerOverrideSpy = jest.spyOn(profUtils.ProfilesUtils, "getCredentialManagerOverride");
getCredentialManagerMapSpy = jest.spyOn(profUtils.ProfilesUtils, "getCredentialManagerMap");
setupCustomCredentialManagerSpy = jest.spyOn((profUtils as any).ProfilesUtils, "setupCustomCredentialManager");
readProfilesFromDiskSpy = jest.spyOn(zowe.imperative.ProfileInfo.prototype, "readProfilesFromDisk");
profileManagerWillLoadSpy = jest.spyOn(zowe.imperative.ProfileInfo.prototype, "profileManagerWillLoad");
promptAndDisableCredentialManagementSpy = jest.spyOn(profUtils.ProfilesUtils, "promptAndDisableCredentialManagement");
});

Expand All @@ -772,7 +754,7 @@ describe("ProfilesUtils unit tests", () => {
credMgrZEName: "test",
});
setupCustomCredentialManagerSpy.mockReturnValueOnce({});
await expect(profUtils.ProfilesUtils.getProfileInfo(false)).resolves.toEqual({});
await expect(profUtils.ProfilesUtils.setupProfileInfo(false)).resolves.toBeInstanceOf(zowe.imperative.ProfileInfo);
expect(isVSCodeCredentialPluginInstalledSpy).toBeCalledTimes(1);
});

Expand All @@ -788,57 +770,31 @@ describe("ProfilesUtils unit tests", () => {
credMgrZEName: "test",
});
setupCustomCredentialManagerSpy.mockReturnValueOnce({});
await expect(profUtils.ProfilesUtils.getProfileInfo(false)).resolves.toEqual({});
await expect(profUtils.ProfilesUtils.setupProfileInfo(false)).resolves.toBeInstanceOf(zowe.imperative.ProfileInfo);
});

it("should retrieve the default credential manager if no custom credential manager is found", async () => {
getDirectValueSpy.mockReturnValueOnce(false);
getDirectValueSpy.mockReturnValueOnce(true).mockReturnValueOnce(false);
getCredentialManagerOverrideSpy.mockReturnValue("@zowe/cli");
isVSCodeCredentialPluginInstalledSpy.mockReturnValueOnce(false);
getDirectValueSpy.mockReturnValueOnce(true);
getCredentialManagerMapSpy.mockReturnValueOnce(undefined);
setupCustomCredentialManagerSpy.mockReturnValueOnce({});
await expect(profUtils.ProfilesUtils.getProfileInfo(false)).resolves.toEqual({});
await expect(profUtils.ProfilesUtils.setupProfileInfo(false)).resolves.toBeInstanceOf(zowe.imperative.ProfileInfo);
});

it("should retrieve the default credential manager and prompt to disable credential management if environment not supported", async () => {
const expectedErrMsg =
// eslint-disable-next-line max-len
"Failed to load credential manager. This may be related to Zowe Explorer being unable to use the default credential manager in a browser based environment.";
getDirectValueSpy.mockReturnValueOnce(false);
getDirectValueSpy.mockReturnValueOnce(true).mockReturnValueOnce(false);
getCredentialManagerOverrideSpy.mockReturnValue("@zowe/cli");
isVSCodeCredentialPluginInstalledSpy.mockReturnValueOnce(false);
getDirectValueSpy.mockReturnValueOnce(true);
getCredentialManagerMapSpy.mockReturnValueOnce(undefined);
setupCustomCredentialManagerSpy.mockReturnValueOnce({});
readProfilesFromDiskSpy.mockImplementation(() => {
const err = new zowe.imperative.ProfInfoErr({
msg: expectedErrMsg,
});
Object.defineProperty(err, "errorCode", {
value: zowe.imperative.ProfInfoErr.LOAD_CRED_MGR_FAILED,
configurable: true,
});
throw err;
});
await expect(profUtils.ProfilesUtils.getProfileInfo(false)).rejects.toThrow(expectedErrMsg);
profileManagerWillLoadSpy.mockReturnValueOnce(false);
promptAndDisableCredentialManagementSpy.mockResolvedValueOnce(undefined);
await expect(profUtils.ProfilesUtils.setupProfileInfo(false)).resolves.toBeInstanceOf(zowe.imperative.ProfileInfo);
expect(promptAndDisableCredentialManagementSpy).toHaveBeenCalledTimes(1);
});

it("should ignore error if it is not an instance of ProfInfoErr", async () => {
const expectedErrorMsg = "Another error unrelated to credential management";
getDirectValueSpy.mockReturnValueOnce(false);
getCredentialManagerOverrideSpy.mockReturnValue("@zowe/cli");
isVSCodeCredentialPluginInstalledSpy.mockReturnValueOnce(false);
getDirectValueSpy.mockReturnValueOnce(true);
getCredentialManagerMapSpy.mockReturnValueOnce(undefined);
setupCustomCredentialManagerSpy.mockReturnValueOnce({});
readProfilesFromDiskSpy.mockImplementation(() => {
throw new Error(expectedErrorMsg);
});
await expect(profUtils.ProfilesUtils.getProfileInfo(false)).resolves.not.toThrow();
expect(promptAndDisableCredentialManagementSpy).toHaveBeenCalledTimes(0);
});
});

describe("isVSCodeCredentialPluginInstalled", () => {
Expand Down Expand Up @@ -916,7 +872,7 @@ describe("ProfilesUtils unit tests", () => {
jest.restoreAllMocks();
});

it("should return the profileInfo object with the custom credential manager constructor", async () => {
it("should return the credential manager override with the custom credential manager constructor", async () => {
const zoweLoggerTraceSpy = jest.spyOn(ZoweLogger, "trace");
const zoweLoggerInfoSpy = jest.spyOn(ZoweLogger, "info");

Expand All @@ -929,7 +885,7 @@ describe("ProfilesUtils unit tests", () => {
credMgrPluginName: "test",
credMgrZEName: "test",
})
).resolves.toEqual({} as zowe.imperative.ProfileInfo);
).resolves.toMatchObject({ service: "test" });
expect(zoweLoggerTraceSpy).toBeCalledTimes(2);
expect(zoweLoggerInfoSpy).toBeCalledTimes(1);
});
Expand Down Expand Up @@ -1079,13 +1035,20 @@ describe("ProfilesUtils unit tests", () => {
});

describe("setupDefaultCredentialManager", () => {
it("calls readProfilesFromDisk with homeDir and projectDir", async () => {
const readProfilesFromDiskMock = jest.spyOn(zowe.imperative.ProfileInfo.prototype, "readProfilesFromDisk").mockImplementation();
it("calls profileManagerWillLoad to load default credential manager", async () => {
const profileManagerWillLoadSpy = jest.spyOn(zowe.imperative.ProfileInfo.prototype, "profileManagerWillLoad");
await profUtils.ProfilesUtils.setupDefaultCredentialManager();
expect(readProfilesFromDiskMock).toHaveBeenCalledWith({
homeDir: zowe.getZoweDir(),
projectDir: vscode.workspace.workspaceFolders?.[0].uri.fsPath,
});
expect(profileManagerWillLoadSpy).toHaveBeenCalled();
});

it("prompts user to disable credential manager if default fails to load", async () => {
const profileManagerWillLoadSpy = jest
.spyOn(zowe.imperative.ProfileInfo.prototype, "profileManagerWillLoad")
.mockResolvedValueOnce(false);
const disableCredMgmtSpy = jest.spyOn(profUtils.ProfilesUtils, "promptAndDisableCredentialManagement").mockImplementation();
await profUtils.ProfilesUtils.setupDefaultCredentialManager();
expect(profileManagerWillLoadSpy).toHaveBeenCalled();
expect(disableCredMgmtSpy).toHaveBeenCalled();
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
"writeOverridesFile.jsonParse.error": "Failed to parse JSON file {0}. Will try to re-create the file.",
"writeOverridesFile.updateFile": "Updating imperative.json Credential Manager to {0}.\n{1}",
"initializeZoweFolder.error": "Failed to initialize Zowe folder: {0}",
"initializeZoweProfiles.success": "Zowe Profiles initialized successfully.",
"initializeZoweProfiles.success": "Zowe profiles initialized successfully.",
"initializeZoweTempFolder.success": "Zowe Temp folder initialized successfully.",
"getProfile.notTreeItem": "Tree Item is not a Zowe Explorer item."
}
1 change: 0 additions & 1 deletion packages/zowe-explorer/src/Profiles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,6 @@ export class Profiles extends ProfilesCache {
await Profiles.loader.refresh(ZoweExplorerApiRegister.getInstance());
} catch (err) {
ZoweLogger.error(err);
ZoweExplorerExtender.showZoweConfigError(err.message);
}
return Profiles.loader;
}
Expand Down
2 changes: 1 addition & 1 deletion packages/zowe-explorer/src/ZoweExplorerExtender.ts
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ export class ZoweExplorerExtender implements ZoweExplorerApi.IApiExplorerExtende
let usingTeamConfig: boolean;
let profileInfo: zowe.imperative.ProfileInfo;
try {
profileInfo = await ProfilesUtils.getProfileInfo(globals.ISTHEIA);
profileInfo = await ProfilesUtils.setupProfileInfo(globals.ISTHEIA);
await profileInfo.readProfilesFromDisk({ homeDir: zoweDir, projectDir });
usingTeamConfig = profileInfo.usingTeamConfig;
} catch (error) {
Expand Down
Loading

0 comments on commit ad31c96

Please sign in to comment.