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

[v2] Port fix for duplicate config watcher events #3378

Merged
merged 3 commits into from
Jan 6, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
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/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ All notable changes to the "zowe-explorer-api" extension will be documented in t
### Bug fixes

- Fixed an issue where the `responseTimeout` profile property was ignored for z/OSMF MVS and USS API calls. [#3225](https://github.com/zowe/zowe-explorer-vscode/issues/3225)
- Updated the `@zowe/cli` dependency to v7.29.7 for technical currency. [#3342](https://github.com/zowe/zowe-explorer-vscode/pull/3342)
- Updated the `@zowe/cli` dependency to v7.29.8 for technical currency. [#3378](https://github.com/zowe/zowe-explorer-vscode/pull/3378)

## `2.18.0`

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
2 changes: 2 additions & 0 deletions packages/zowe-explorer-ftp-extension/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ All notable changes to the "zowe-explorer-ftp-extension" extension will be docum

### Bug fixes

- Updated the `@zowe/cli` dependency to v7.29.8 for technical currency. [#3378](https://github.com/zowe/zowe-explorer-vscode/pull/3378)

## `2.18.0`

### Bug fixes
Expand Down
2 changes: 2 additions & 0 deletions packages/zowe-explorer/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ All notable changes to the "vscode-extension-for-zowe" extension will be documen
- 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 "Open with Encoding" menu failed when tagged encoding was selected for a binary USS file. [#3377](https://github.com/zowe/zowe-explorer-vscode/pull/3377)
- Fixed an issue where editing a team config file or updating secrets in the OS credential vault could trigger multiple events for a single action. [#3378](https://github.com/zowe/zowe-explorer-vscode/pull/3378)
- Updated the `@zowe/cli` dependency to v7.29.8 for technical currency. [#3378](https://github.com/zowe/zowe-explorer-vscode/pull/3378)

## `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 @@ -1372,3 +1372,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."
}
Loading
Loading