From 38b35f4f4e7c7b3b54172d08a245873d888ebf48 Mon Sep 17 00:00:00 2001 From: Trae Yelovich Date: Thu, 25 Jul 2024 17:14:54 -0400 Subject: [PATCH] fix(tests): Resolve failing unit tests Signed-off-by: Trae Yelovich --- .../__tests__/__mocks__/mockCreators/jobs.ts | 1 - .../__tests__/__mocks__/vscode.ts | 169 ++++++++++++++++++ .../__tests__/__unit__/extension.unit.test.ts | 1 + .../trees/job/JobActions.unit.test.ts | 32 +++- .../src/configuration/Constants.ts | 2 +- 5 files changed, 195 insertions(+), 10 deletions(-) diff --git a/packages/zowe-explorer/__tests__/__mocks__/mockCreators/jobs.ts b/packages/zowe-explorer/__tests__/__mocks__/mockCreators/jobs.ts index 461372ad2f..97d29c3d92 100644 --- a/packages/zowe-explorer/__tests__/__mocks__/mockCreators/jobs.ts +++ b/packages/zowe-explorer/__tests__/__mocks__/mockCreators/jobs.ts @@ -145,7 +145,6 @@ export function createJobNode(session: any, profile: imperative.IProfileLoaded) label: "sampleJob", collapsibleState: vscode.TreeItemCollapsibleState.Collapsed, parentNode: session.getSessionNode(), - session, profile, job: createIJobObject(), }); diff --git a/packages/zowe-explorer/__tests__/__mocks__/vscode.ts b/packages/zowe-explorer/__tests__/__mocks__/vscode.ts index b7028bcc7b..2d3aa4dbfd 100644 --- a/packages/zowe-explorer/__tests__/__mocks__/vscode.ts +++ b/packages/zowe-explorer/__tests__/__mocks__/vscode.ts @@ -389,7 +389,176 @@ export interface FileDecorationProvider { provideFileDecoration(uri: Uri, token: CancellationToken): ProviderResult; } +/** + * Additional information the webview view being resolved. + * + * @param T Type of the webview's state. + */ +interface WebviewViewResolveContext { + /** + * Persisted state from the webview content. + * + * To save resources, the editor normally deallocates webview documents (the iframe content) that are not visible. + * For example, when the user collapse a view or switches to another top level activity in the sidebar, the + * `WebviewView` itself is kept alive but the webview's underlying document is deallocated. It is recreated when + * the view becomes visible again. + * + * You can prevent this behavior by setting `retainContextWhenHidden` in the `WebviewOptions`. However this + * increases resource usage and should be avoided wherever possible. Instead, you can use persisted state to + * save off a webview's state so that it can be quickly recreated as needed. + * + * To save off a persisted state, inside the webview call `acquireVsCodeApi().setState()` with + * any json serializable object. To restore the state again, call `getState()`. For example: + * + * ```js + * // Within the webview + * const vscode = acquireVsCodeApi(); + * + * // Get existing state + * const oldState = vscode.getState() || { value: 0 }; + * + * // Update state + * setState({ value: oldState.value + 1 }) + * ``` + * + * The editor ensures that the persisted state is saved correctly when a webview is hidden and across + * editor restarts. + */ + readonly state: T | undefined; +} + +/** + * A webview based view. + */ +export interface WebviewView { + /** + * Identifies the type of the webview view, such as `'hexEditor.dataView'`. + */ + readonly viewType: string; + + /** + * The underlying webview for the view. + */ + readonly webview: any; + + /** + * View title displayed in the UI. + * + * The view title is initially taken from the extension `package.json` contribution. + */ + title?: string; + + /** + * Human-readable string which is rendered less prominently in the title. + */ + description?: string; + + /** + * The badge to display for this webview view. + * To remove the badge, set to undefined. + */ + badge?: any; + + /** + * Event fired when the view is disposed. + * + * Views are disposed when they are explicitly hidden by a user (this happens when a user + * right clicks in a view and unchecks the webview view). + * + * Trying to use the view after it has been disposed throws an exception. + */ + readonly onDidDispose: Event; + + /** + * Tracks if the webview is currently visible. + * + * Views are visible when they are on the screen and expanded. + */ + readonly visible: boolean; + + /** + * Event fired when the visibility of the view changes. + * + * Actions that trigger a visibility change: + * + * - The view is collapsed or expanded. + * - The user switches to a different view group in the sidebar or panel. + * + * Note that hiding a view using the context menu instead disposes of the view and fires `onDidDispose`. + */ + readonly onDidChangeVisibility: Event; + + /** + * Reveal the view in the UI. + * + * If the view is collapsed, this will expand it. + * + * @param preserveFocus When `true` the view will not take focus. + */ + show(preserveFocus?: boolean): void; +} + +/** + * Provider for creating `WebviewView` elements. + */ +export interface WebviewViewProvider { + /** + * Resolves a webview view. + * + * `resolveWebviewView` is called when a view first becomes visible. This may happen when the view is + * first loaded or when the user hides and then shows a view again. + * + * @param webviewView Webview view to restore. The provider should take ownership of this view. The + * provider must set the webview's `.html` and hook up all webview events it is interested in. + * @param context Additional metadata about the view being resolved. + * @param token Cancellation token indicating that the view being provided is no longer needed. + * + * @returns Optional thenable indicating that the view has been fully resolved. + */ + resolveWebviewView(webviewView: WebviewView, context: WebviewViewResolveContext, token: CancellationToken): Thenable | void; +} + export namespace window { + /** + * Register a new provider for webview views. + * + * @param viewId Unique id of the view. This should match the `id` from the + * `views` contribution in the package.json. + * @param provider Provider for the webview views. + * + * @returns Disposable that unregisters the provider. + */ + export function registerWebviewViewProvider( + viewId: string, + provider: WebviewViewProvider, + options?: { + /** + * Content settings for the webview created for this view. + */ + readonly webviewOptions?: { + /** + * Controls if the webview element itself (iframe) is kept around even when the view + * is no longer visible. + * + * Normally the webview's html context is created when the view becomes visible + * and destroyed when it is hidden. Extensions that have complex state + * or UI can set the `retainContextWhenHidden` to make the editor keep the webview + * context around, even when the webview moves to a background tab. When a webview using + * `retainContextWhenHidden` becomes hidden, its scripts and other dynamic content are suspended. + * When the view becomes visible again, the context is automatically restored + * in the exact same state it was in originally. You cannot send messages to a + * hidden webview, even with `retainContextWhenHidden` enabled. + * + * `retainContextWhenHidden` has a high memory overhead and should only be used if + * your view's context cannot be quickly saved and restored. + */ + readonly retainContextWhenHidden?: boolean; + }; + } + ): Disposable { + return new Disposable(); + } + export const visibleTextEditors = []; /** * Options for creating a {@link TreeView} diff --git a/packages/zowe-explorer/__tests__/__unit__/extension.unit.test.ts b/packages/zowe-explorer/__tests__/__unit__/extension.unit.test.ts index 34ba67d079..6ad3ed7a75 100644 --- a/packages/zowe-explorer/__tests__/__unit__/extension.unit.test.ts +++ b/packages/zowe-explorer/__tests__/__unit__/extension.unit.test.ts @@ -209,6 +209,7 @@ function createGlobalMocks() { "zowe.jobs.sortBy", "zowe.jobs.filterJobs", "zowe.jobs.copyName", + "zowe.jobs.tabularView", "zowe.updateSecureCredentials", "zowe.manualPoll", "zowe.editHistory", diff --git a/packages/zowe-explorer/__tests__/__unit__/trees/job/JobActions.unit.test.ts b/packages/zowe-explorer/__tests__/__unit__/trees/job/JobActions.unit.test.ts index 5a10debfd4..e13f60b095 100644 --- a/packages/zowe-explorer/__tests__/__unit__/trees/job/JobActions.unit.test.ts +++ b/packages/zowe-explorer/__tests__/__unit__/trees/job/JobActions.unit.test.ts @@ -111,7 +111,7 @@ function createGlobalMocks() { get: activeTextEditorDocument, configurable: true, }); - Object.defineProperty(Profiles, "getInstance", { value: jest.fn().mockResolvedValue(newMocks.mockProfileInstance), configurable: true }); + Object.defineProperty(Profiles, "getInstance", { value: jest.fn().mockReturnValue(newMocks.mockProfileInstance), configurable: true }); const executeCommand = jest.fn(); Object.defineProperty(vscode.commands, "executeCommand", { value: executeCommand, configurable: true }); Object.defineProperty(ZoweLogger, "error", { value: jest.fn(), configurable: true }); @@ -1130,9 +1130,20 @@ describe("cancelJob", () => { createGlobalMocks(); const session = createISession(); const profile = createIProfile(); - const jobSessionNode = createJobSessionNode(session, profile); - const jobNode = createJobNode(jobSessionNode, profile); - const jobsProvider = createJobsTree(session, jobNode.job, profile, createTreeView()); + + const createBlockMocks = () => { + const jobSessionNode = createJobSessionNode(session, profile); + const jobNode = createJobNode(jobSessionNode, profile); + + return { + session: createISession(), + profile, + jobSessionNode, + jobNode, + jobsProvider: createJobsTree(session, jobNode.job, profile, createTreeView()), + }; + }; + const jesCancelJobMock = jest.fn(); const mockJesApi = (mockFn?: jest.Mock): void => { @@ -1155,17 +1166,20 @@ describe("cancelJob", () => { }); it("returns early if no nodes are specified", async () => { + const { jobsProvider } = createBlockMocks(); await JobActions.cancelJobs(jobsProvider, []); expect(Gui.showMessage).not.toHaveBeenCalled(); }); it("returns early if all nodes in selection have been cancelled", async () => { + const { jobNode, jobsProvider } = createBlockMocks(); jobNode.job.retcode = "CANCELED"; await JobActions.cancelJobs(jobsProvider, [jobNode]); expect(Gui.showMessage).toHaveBeenCalledWith("The selected jobs were already cancelled."); }); it("shows a warning message if one or more jobs failed to cancel", async () => { + const { jobNode, jobsProvider } = createBlockMocks(); jobNode.job.retcode = "ACTIVE"; jesCancelJobMock.mockResolvedValueOnce(false); await JobActions.cancelJobs(jobsProvider, [jobNode]); @@ -1175,6 +1189,7 @@ describe("cancelJob", () => { }); it("shows a warning message if one or more APIs do not support cancelJob", async () => { + const { jobNode, jobsProvider } = createBlockMocks(); // Make cancelJob undefined mockJesApi(); jobNode.job.retcode = "ACTIVE"; @@ -1188,6 +1203,7 @@ describe("cancelJob", () => { }); it("shows matching error messages for one or more failed jobs", async () => { + const { jobNode, jobsProvider } = createBlockMocks(); jobNode.job.retcode = "ACTIVE"; jesCancelJobMock.mockRejectedValueOnce(new Error("Failed to cancel job... something went wrong.")); await JobActions.cancelJobs(jobsProvider, [jobNode]); @@ -1200,18 +1216,18 @@ describe("cancelJob", () => { }); it("shows a message confirming the jobs were cancelled", async () => { + const { jobNode, jobsProvider, jobSessionNode } = createBlockMocks(); jobNode.job.retcode = "ACTIVE"; jesCancelJobMock.mockResolvedValueOnce(true); - const setImmediateSpy = jest.spyOn(global, "setImmediate"); + const getChildrenMock = jest.spyOn(jobSessionNode, "getChildren"); await JobActions.cancelJobs(jobsProvider, [jobNode]); - + expect(getChildrenMock).toHaveBeenCalled(); // Check that refreshElement was called through setImmediate - expect(setImmediateSpy).toHaveBeenCalled(); - expect(Gui.showMessage).toHaveBeenCalledWith("Cancelled selected jobs successfully."); }); it("does not work for job session nodes", async () => { + const { jobsProvider, jobSessionNode } = createBlockMocks(); await JobActions.cancelJobs(jobsProvider, [jobSessionNode]); expect(jesCancelJobMock).not.toHaveBeenCalled(); }); diff --git a/packages/zowe-explorer/src/configuration/Constants.ts b/packages/zowe-explorer/src/configuration/Constants.ts index 6e796f1175..7db56f034b 100644 --- a/packages/zowe-explorer/src/configuration/Constants.ts +++ b/packages/zowe-explorer/src/configuration/Constants.ts @@ -17,7 +17,7 @@ import type { Profiles } from "./Profiles"; export class Constants { public static CONFIG_PATH: string; - public static readonly COMMAND_COUNT = 99; + public static readonly COMMAND_COUNT = 100; public static readonly MAX_SEARCH_HISTORY = 5; public static readonly MAX_FILE_HISTORY = 10; public static readonly MS_PER_SEC = 1000;