Skip to content

Commit

Permalink
Merge pull request #2136 from zowe/custEventsPt2
Browse files Browse the repository at this point in the history
Event Operator and Processor
  • Loading branch information
zFernand0 authored Jul 11, 2024
2 parents 8a47262 + 8cb0253 commit 894607b
Show file tree
Hide file tree
Showing 51 changed files with 1,723 additions and 1,150 deletions.
12 changes: 11 additions & 1 deletion packages/imperative/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,16 @@

All notable changes to the Imperative package will be documented in this file.

## Recent Changes

- Enhancement: Add client-side custom-event handling capabilities. [#2136](https://github.com/zowe/zowe-cli/pull/2136)
- Next-Breaking: Refactored the Imperative Event Emitter class. [#2136](https://github.com/zowe/zowe-cli/pull/2136)
- Removed the `ImperativeEventEmitter` class.
- Added an `EventProcessor` class to handle event listening and emitting.
- Added an `EventOperator` class to handle creation and deletion of `EventProcessors`.
- Added an `EventUtils` class to contain all common utility methods for the Client Event Handling capabilities.
- Added `IEmitter`, `IWatcher`, and `IEmitterAndWatcher` interfaces to expose what application developers should see.

## `8.0.0-next.202407051717`

- BugFix: V3 Breaking: Modified the ConvertV1Profiles.convert API to accept a new ProfileInfo option and initialize components sufficiently to enable VSCode apps to convert V1 profiles. [#2170](https://github.com/zowe/zowe-cli/issues/2170)
Expand Down Expand Up @@ -80,7 +90,7 @@ All notable changes to the Imperative package will be documented in this file.

## `8.0.0-next.202405151329`

- Enhancement: Add client-side event handling capabilities. [#1987](https://github.com/zowe/zowe-cli/pull/1987)
- Enhancement: Add client-side event handling capabilities. [#1987](https://github.com/zowe/zowe-cli/issues/1987)

## `8.0.0-next.202405061946`

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@ describe("cmd-cli invalid profile-spec", () => {

it("should fail the command if the profile property is not supplied and the handler requests a profile", () => {
const response = runCliScript(__dirname + "/__scripts__/profile-spec.sh", TEST_ENVIRONMENT.workingDir);
expect(response.status).toBe(1);
expect(response.stdout.toString()).toBe('');
expect(response.stderr.toString()).toContain('Internal Error: No profiles of type "blah" were loaded for this command.');
expect(response.status).toBe(1);
expect(response.stderr.toString()).toContain('This error can occur for one of two reasons:');
expect(response.stderr.toString()).toContain('- The "profile" property on the command definition document ' +
'does NOT specify the requested profile type');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ describe("cmd-cli nested first-group", () => {

it("should display both groups and commands in the help", () => {
const response = runCliScript(__dirname + "/__scripts__/first-group/first_group_help.sh", TEST_ENVIRONMENT.workingDir);
expect(response.status).toBe(0);
expect(response.stderr.toString()).toBe("");
expect(response.status).toBe(0);
expect(response.stdout.toString()).toMatchSnapshot();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ describe("cmd-cli nested", () => {

it("should display both groups and commands in the help", () => {
const response = runCliScript(__dirname + "/__scripts__/nested/nested_help.sh", TEST_ENVIRONMENT.workingDir);
expect(response.status).toBe(0);
expect(response.stderr.toString()).toBe("");
expect(response.status).toBe(0);
expect(response.stdout.toString()).toMatchSnapshot();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ describe("cmd-cli config auto-init", () => {
expect(response.status).toBe(0);

expect(glob.sync("*.json", { cwd: TEST_ENVIRONMENT.workingDir }))
.toEqual(["imperative-test-cli.config.json", "imperative-test-cli.schema.json"]);
.toEqual(["extenders.json", "imperative-test-cli.config.json", "imperative-test-cli.schema.json"]);
const configJson: IConfig = jsonfile.readFileSync(TEST_ENVIRONMENT.workingDir + "/imperative-test-cli.config.json");
expect(configJson.profiles.base_fruit).toBeDefined();
expect(configJson.profiles.base_fruit.properties.tokenValue).toBeUndefined();
Expand All @@ -135,7 +135,7 @@ describe("cmd-cli config auto-init", () => {
expect(response.status).toBe(0);

expect(glob.sync("*.json", { cwd: TEST_ENVIRONMENT.workingDir }))
.toEqual(["imperative-test-cli.config.user.json", "imperative-test-cli.schema.json"]);
.toEqual(["extenders.json", "imperative-test-cli.config.user.json", "imperative-test-cli.schema.json"]);
const configJson: IConfig = jsonfile.readFileSync(TEST_ENVIRONMENT.workingDir + "/imperative-test-cli.config.user.json");
expect(configJson.profiles.base_fruit).toBeDefined();
expect(configJson.profiles.base_fruit.properties.tokenValue).toBeUndefined();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ describe("imperative-test-cli config convert-profiles", () => {
testName: "imperative_test_cli_test_config_convert_profiles_command"
});
configJsonPath = path.join(process.env.IMPERATIVE_TEST_CLI_CLI_HOME as string, "imperative-test-cli.config.json");
// jest.spyOn(EventOperator, "getZoweProcessor").mockReturnValue({emitZoweEvent: jest.fn()} as any);
});

beforeEach(() => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ import { join } from "path";
jest.mock("../src/syntax/SyntaxValidator");
jest.mock("../src/utils/SharedOptions");
jest.mock("../../utilities/src/ImperativeConfig");
jest.mock("../../events/src/ImperativeEventEmitter");

// Persist the original definitions of process.write
const ORIGINAL_STDOUT_WRITE = process.stdout.write;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,6 @@ import { IConfig } from "../src/doc/IConfig";
import { IConfigLayer } from "../src/doc/IConfigLayer";
import { IConfigProfile } from "../src/doc/IConfigProfile";

jest.mock("../../events/src/ImperativeEventEmitter");

const MY_APP = "my_app";

const mergeConfig: IConfig = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ import { Config } from "../src/Config";
import { IConfig } from "../src/doc/IConfig";
import { IConfigSecure } from "../src/doc/IConfigSecure";
import { IConfigVault } from "../src/doc/IConfigVault";
import { ImperativeEventEmitter } from "../../events";

const MY_APP = "my_app";

Expand Down Expand Up @@ -47,9 +46,6 @@ describe("Config secure tests", () => {
});

beforeEach(() => {
jest.spyOn(ImperativeEventEmitter, "initialize").mockImplementation();
Object.defineProperty(ImperativeEventEmitter, "instance", { value: { emitEvent: jest.fn() }});

mockSecureLoad = jest.fn();
mockSecureSave = jest.fn();
mockVault = {
Expand Down
2 changes: 0 additions & 2 deletions packages/imperative/src/config/__tests__/Config.unit.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,6 @@ import * as JSONC from "comment-json";
import { ConfigLayers, ConfigSecure } from "../src/api";


jest.mock("../../events/src/ImperativeEventEmitter");

const MY_APP = "my_app";

describe("Config tests", () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
*/

jest.mock("../../logger/src/LoggerUtils");
jest.mock("../../events/src/ImperativeEventEmitter");

import { AbstractAuthHandler } from "../../imperative/src/auth/handlers/AbstractAuthHandler";
import { SessConstants } from "../../rest";
Expand Down
118 changes: 117 additions & 1 deletion packages/imperative/src/config/__tests__/ConfigUtils.unit.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,20 @@
*/

import * as fs from "fs";

import * as path from "path";
import * as os from "os";
import * as jsonfile from "jsonfile";
import { ConfigUtils } from "../../config/src/ConfigUtils";
import { CredentialManagerFactory } from "../../security";
import { ImperativeConfig } from "../../utilities";
import { EnvironmentalVariableSettings } from "../../imperative/src/env/EnvironmentalVariableSettings";
import { IExtendersJsonOpts } from "../src/doc/IExtenderOpts";

describe("Config Utils", () => {
afterEach(() => {
jest.restoreAllMocks();
});

describe("coercePropValue", () => {
it("should parse value when type is boolean", () => {
expect(ConfigUtils.coercePropValue("false", "boolean")).toBe(false);
Expand Down Expand Up @@ -149,4 +157,112 @@ describe("Config Utils", () => {
expect(fsExistsSyncSpy).toHaveBeenCalledTimes(1);
});
});

describe("getZoweDir", () => {
const expectedLoadedConfig = {
name: "zowe",
defaultHome: path.join("z", "zowe"),
envVariablePrefix: "ZOWE"
};
let defaultHome: string;
let envReadSpy: any;
let homeDirSpy: any;
let loadedConfigOrig: any;

beforeAll(() => {
loadedConfigOrig = ImperativeConfig.instance.loadedConfig;
});

beforeEach(() => {
envReadSpy = jest.spyOn(EnvironmentalVariableSettings, "read").mockReturnValue({
cliHome: { value: null }
} as any);
homeDirSpy = jest.spyOn(os, "homedir").mockReturnValue(expectedLoadedConfig.defaultHome);
ImperativeConfig.instance.loadedConfig = undefined as any;
defaultHome = path.join(expectedLoadedConfig.defaultHome, ".zowe");
});

afterAll(() => {
ImperativeConfig.instance.loadedConfig = loadedConfigOrig;
envReadSpy.mockRestore();
homeDirSpy.mockRestore();
});

it("should return the ENV cliHome even if loadedConfig is set in the process", () => {
jest.spyOn(EnvironmentalVariableSettings, "read").mockReturnValue({ cliHome: { value: "test" } } as any);
expect(ImperativeConfig.instance.loadedConfig).toBeUndefined();
expect(ConfigUtils.getZoweDir()).toEqual("test");
expect(ImperativeConfig.instance.loadedConfig).toEqual({ ...expectedLoadedConfig, defaultHome });
});

it("should return the defaultHome and set loadedConfig if undefined", () => {
expect(ImperativeConfig.instance.loadedConfig).toBeUndefined();
expect(ConfigUtils.getZoweDir()).toEqual(defaultHome);
expect(ImperativeConfig.instance.loadedConfig).toEqual({ ...expectedLoadedConfig, defaultHome });
});

it("should return the defaultHome and reset loadedConfig if defaultHome changes", () => {
expect(ImperativeConfig.instance.loadedConfig).toBeUndefined();
ImperativeConfig.instance.loadedConfig = { ...expectedLoadedConfig, defaultHome: "test" };
expect(ImperativeConfig.instance.loadedConfig?.defaultHome).toEqual("test");
expect(ConfigUtils.getZoweDir()).toEqual(defaultHome);
expect(ImperativeConfig.instance.loadedConfig).toEqual({ ...expectedLoadedConfig, defaultHome });
});

it("should return the defaultHome without resetting loadedConfig", () => {
expect(ImperativeConfig.instance.loadedConfig).toBeUndefined();
ImperativeConfig.instance.loadedConfig = expectedLoadedConfig;
expect(ConfigUtils.getZoweDir()).toEqual(defaultHome);
expect(ImperativeConfig.instance.loadedConfig).toEqual({ ...expectedLoadedConfig, defaultHome });
});
});

const dummyExtJson: IExtendersJsonOpts = {
profileTypes: {
"test": {
from: ["Zowe Client App"]
}
}
};
describe("readExtendersJsonFromDisk", () => {
// case 1: the JSON file doesn't exist at time of read
it("writes an empty extenders.json file if it doesn't exist on disk", async () => {
const writeFileSyncMock = jest.spyOn(jsonfile, "writeFileSync").mockImplementation();
jest.spyOn(fs, "existsSync").mockReturnValueOnce(false);
ConfigUtils.readExtendersJson();
expect(writeFileSyncMock).toHaveBeenCalled();
});

// case 2: JSON file exists on-disk at time of read
it("reads extenders.json from disk if it exists", async () => {
const readFileSyncMock = jest.spyOn(jsonfile, "readFileSync").mockReturnValueOnce(dummyExtJson);
jest.spyOn(fs, "existsSync").mockReturnValueOnce(true);
const result = ConfigUtils.readExtendersJson();
expect(readFileSyncMock).toHaveBeenCalled();
expect(result).toEqual({
profileTypes: {
"test": {
from: ["Zowe Client App"]
}
}
});
});
});

describe("writeExtendersJson", () => {
// case 1: Write operation is successful
it("returns true if written to disk successfully", async () => {
const writeFileSyncMock = jest.spyOn(jsonfile, "writeFileSync").mockImplementation();
expect(ConfigUtils.writeExtendersJson(dummyExtJson)).toBe(true);
expect(writeFileSyncMock).toHaveBeenCalled();
});

// case 2: Write operation is unsuccessful
it("returns false if it couldn't write to disk", async () => {
const writeFileSyncMock = jest.spyOn(jsonfile, "writeFileSync").mockImplementation();
writeFileSyncMock.mockImplementation(() => { throw new Error(); });
expect(ConfigUtils.writeExtendersJson(dummyExtJson)).toBe(false);
expect(writeFileSyncMock).toHaveBeenCalled();
});
});
});
Loading

0 comments on commit 894607b

Please sign in to comment.